mirror of
https://github.com/russolsen/sallyforth
synced 2024-12-25 21:58:18 +01:00
Added better support for calling native code, added readline history, else to ifs.
This commit is contained in:
parent
d5e5885b0b
commit
6925755432
5 changed files with 258 additions and 30 deletions
|
@ -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 += ' '
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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!")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue