Added better support for calling native code, added readline history, else to ifs.

This commit is contained in:
Russ Olsen 2020-04-17 08:56:06 -04:00
parent d5e5885b0b
commit 6925755432
5 changed files with 258 additions and 30 deletions

View file

@ -13,14 +13,18 @@ class Compiler:
def offset(self):
return len(self.instructions)
def push_offset(self):
def push_offset(self, value=None):
if not value:
self.offsets.push(self.offset())
else:
self.offsets.push(value)
#print("compiler stack", self.offsets.stack)
def pop_offset(self):
return self.offsets.pop()
def _str__(self):
result = f'Compiler {name} {immediate} '
result = f'Compiler {name}'
for i in self.instructions:
result += str(i)
result += ' '

View file

@ -1,6 +1,7 @@
import sys
from os import path
from words import *
import words
from lex import forth_prompt, read_tokens, is_string, tokenize
from stack import Stack
@ -27,13 +28,16 @@ class Forth:
'true': const_f(True),
'false': const_f(False),
'nil': const_f(None),
'def': w_def,
'import': w_import,
'0': const_f(0),
'1': const_f(1),
'2': const_f(2),
';': w_semi,
':': w_colon,
'->list': w_list,
'[': w_startlist,
']': w_endlist,
'{': w_startmap,
'}': w_endmap,
'+': w_add,
'+': w_add,
'-': w_sub,
@ -43,22 +47,22 @@ class Forth:
'<=': w_le,
'>=': w_ge,
'=': w_eq,
'dup': w_dup,
'swap': w_swap,
'.': w_dot,
'nl': w_nl,
'dump': w_dump,
'idump': w_idump,
'stack': w_stack,
'begin': w_begin,
'until': w_until,
'if': w_if,
'then': w_then}
'.': w_dot}
self.import_from_module(words, 'w_')
self.compiler = None
if startup:
execute_startup(startup)
def import_from_module(self, m, prefix):
names = dir(m)
prefix_len = len(prefix)
for name in names:
if name.startswith(prefix):
word_name = name[prefix_len::]
self.dictionary[word_name] = m.__getattribute__(name)
def defvar(self, name, value):
self.dictionary[name] = const_f(value)
@ -81,6 +85,7 @@ class Forth:
self.compile_token(token)
def execute_file(self, fpath):
old_source = self.dictionary.get('*source*', None)
self.defvar('*source*', fpath)
with open(fpath) as f:
line = f.readline()
@ -89,6 +94,7 @@ class Forth:
self.execute_tokens(tokens)
line = f.readline()
self.defvar('*source*', '')
self.dictionary['*source*'] = old_source
def compile_token(self, token):
if self.compiler.name == None:

View file

@ -1,19 +1,62 @@
import os
import sys
from kernel import Forth
from lex import tokenize
import readline
HistoryFile=".sallyforth"
histfile = os.path.join(os.path.expanduser("~"), HistoryFile)
try:
readline.read_history_file(histfile)
except FileNotFoundError:
pass
source_dir = os.path.dirname(os.path.abspath(__file__))
startup_file = f'{source_dir}/startup.sf'
print(startup_file)
f = Forth()
class Completer:
def __init__(self, f):
self.f = f
def complete(self, prefix, index):
self.matching_words = [w for w in self.f.dictionary.keys() if w.startswith(prefix) ]
try:
return self.matching_words[index]
except IndexError:
return None
completer = Completer(f)
readline.parse_and_bind("tab: complete")
readline.set_completer(completer.complete)
f.defvar("argv", sys.argv[1::])
if os.path.exists(startup_file):
f.execute_file(startup_file)
while True:
p = f.evaluate_token('*prompt*')
try:
line = input(p)
except KeyboardInterrupt:
print("<<interrupt>>")
line = ''
except EOFError:
break
tokens = tokenize(line)
try:
f.execute_tokens(tokens)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
print("Error:", exc_type)
print("Error:", exc_value)
print("Error:", exc_traceback)
readline.write_history_file(histfile)
print("Bye!")

View file

@ -1,10 +1,37 @@
"Executing " . *source* . nl
: prompt "Yo> " ;
"time" require
"math" require
"sys" require
"os" require
"os.path" require
"io" require
: *prompt* "SF> " ;
: [] ( -- <empty list>) [ ] ;
: px0 (mod fname -- result) [] px ;
: px1 (mod fname arg -- result) 1 ->list px ;
: px2 (mod fname arg arg -- result) 2 ->list px ;
: px3 (mod fname arg arg arg -- result) 3 ->list px ;
: dir (mod -- keys) "dir" px0 ;
: ctime [] time.ctime ;
: sleep 1 ->list stack time.sleep drop ;
: hello (simple greeting) "Hello" . nl ;
: >0 0 > ;
: <0 0 < ;
: p dup . nl ;
: p . nl ;
: top dup p ;
: source-if-exists
dup
1 ->list os.path.exists
if source else drop then
;
"init.sf" source-if-exists

View file

@ -1,5 +1,45 @@
from compiler import Compiler
import importlib
from inspect import isfunction, isbuiltin
def const_f(value):
def x(f):
f.stack.push(value)
return 1
return x
def native_function_handler(func):
def handle(forth):
args = forth.stack.pop()
result = func(*args)
forth.stack.push(result)
return 1
return handle
def import_native_module(forth, m, alias=None, excludes=[]):
if not alias:
alias = m.__name__
raw_names = dir(m)
names = [x for x in raw_names if x not in excludes]
for name in names:
localname = f'{alias}.{name}'
print(localname)
val = m.__getattribute__(name)
if isfunction(val) or isbuiltin(val):
forth.dictionary[localname] = native_function_handler(val)
else:
forth.dictionary[localname] = const_f(val)
def w_require(f):
name = f.stack.pop()
m = importlib.import_module(name)
import_native_module(f, m, name)
return 1
def w_source(f):
path = f.stack.pop()
f.execute_file(path)
return 1
def execute_f(name, instructions):
# print("execute_f:", len(instructions))
@ -27,22 +67,98 @@ def ifnot_jump_f(n):
return 1
return ifnot_jump
def const_f(value):
def x(f):
f.stack.push(value)
return 1
return x
def jump_f(n):
def do_jump(forth):
return n
return do_jump
def w_import(f):
name = f.stack.pop()
m = importlib.import_module(name)
f.stack.push(m)
f.dictionary[name] = const_f(m)
return 1
def w_px(f):
args = f.stack.pop()
print("args", args)
name = f.stack.pop()
print("name", name)
m = f.stack.pop()
print("mod:", m)
func = m.__dict__[name]
print("f:", f);
result = func(*args)
print("result", result)
f.stack.push(result)
return 1
def w_list(f):
n = f.stack.pop()
l = []
for i in range(n):
l.append(f.stack.pop())
print(l)
f.stack.push(l)
return 1
ListMarker = object()
def w_startlist(f): # [
f.stack.push(ListMarker)
return 1
def w_endlist(f): # ]
l = []
x = f.stack.pop()
while x != ListMarker:
l.append(x)
x = f.stack.pop()
l.reverse()
f.stack.push(l)
return 1
MapMarker = object()
def w_startmap(f): # {
f.stack.push(MapMarker)
return 1
def w_endmap(f): # }
l = []
x = f.stack.pop()
while x != MapMarker:
l.append(x)
x = f.stack.pop()
if (len(l) % 2) != 0:
print("Maps need even number of entries.")
return 1
l.reverse()
result = {}
for i in range(0, len(l), 2):
result[l[i]] = l[i+1]
f.stack.push(result)
return 1
def w_get(f):
name = f.stack.pop()
m = f.stack.pop()
result = m[name]
f.stack.push(result)
return 1
def w_getattribute(f):
name = f.stack.pop()
x = f.stack.pop()
result = x.__getattribute__(name)
f.stack.push(result)
return 1
def w_def(f):
value = f.stack.pop()
name = f.stack.pop()
f.defvar(name, value)
print('name', name, 'value', value)
return 1
def w_gt(f):
a = f.stack.pop()
@ -108,6 +224,19 @@ def w_dup(f):
f.stack.push(x)
return 1
def w_rot(f):
c = f.stack.pop()
b = f.stack.pop()
a = f.stack.pop()
f.stack.push(b)
f.stack.push(c)
f.stack.push(a)
return 1
def w_drop(f):
f.stack.pop()
return 1
def w_swap(f):
a = f.stack.pop()
b = f.stack.pop()
@ -143,6 +272,7 @@ def w_if(forth):
print("w_if")
compiler = forth.compiler
compiler.push_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return 1
@ -150,14 +280,31 @@ w_if.__dict__['immediate'] = True
def w_then(forth):
compiler = forth.compiler
else_offset = compiler.pop_offset()
if_offset = compiler.pop_offset()
end_offset = compiler.offset()
delta = end_offset - if_offset
then_offset = compiler.offset()
if else_offset == if_offset:
delta = then_offset - if_offset
compiler.instructions[if_offset] = ifnot_jump_f(delta)
else:
if_delta = else_offset - if_offset + 1
compiler.instructions[if_offset] = ifnot_jump_f(if_delta)
else_delta = then_offset - else_offset
compiler.instructions[else_offset] = jump_f(else_delta)
return 1
w_then.__dict__['immediate'] = True
def w_else(forth):
compiler = forth.compiler
compiler.pop_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return 1
w_else.__dict__['immediate'] = True
def w_do(forth):
print("w_do")
compiler = forth.compiler
@ -196,6 +343,7 @@ def w_until(forth):
w_until.__dict__['immediate'] = True
def w_dump(f):
f.dump()
return 1