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):
|
def offset(self):
|
||||||
return len(self.instructions)
|
return len(self.instructions)
|
||||||
|
|
||||||
def push_offset(self):
|
def push_offset(self, value=None):
|
||||||
self.offsets.push(self.offset())
|
if not value:
|
||||||
|
self.offsets.push(self.offset())
|
||||||
|
else:
|
||||||
|
self.offsets.push(value)
|
||||||
|
#print("compiler stack", self.offsets.stack)
|
||||||
|
|
||||||
def pop_offset(self):
|
def pop_offset(self):
|
||||||
return self.offsets.pop()
|
return self.offsets.pop()
|
||||||
|
|
||||||
def _str__(self):
|
def _str__(self):
|
||||||
result = f'Compiler {name} {immediate} '
|
result = f'Compiler {name}'
|
||||||
for i in self.instructions:
|
for i in self.instructions:
|
||||||
result += str(i)
|
result += str(i)
|
||||||
result += ' '
|
result += ' '
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
from os import path
|
from os import path
|
||||||
from words import *
|
from words import *
|
||||||
|
import words
|
||||||
from lex import forth_prompt, read_tokens, is_string, tokenize
|
from lex import forth_prompt, read_tokens, is_string, tokenize
|
||||||
from stack import Stack
|
from stack import Stack
|
||||||
|
|
||||||
|
@ -27,13 +28,16 @@ class Forth:
|
||||||
'true': const_f(True),
|
'true': const_f(True),
|
||||||
'false': const_f(False),
|
'false': const_f(False),
|
||||||
'nil': const_f(None),
|
'nil': const_f(None),
|
||||||
'def': w_def,
|
|
||||||
'import': w_import,
|
|
||||||
'0': const_f(0),
|
'0': const_f(0),
|
||||||
'1': const_f(1),
|
'1': const_f(1),
|
||||||
'2': const_f(2),
|
'2': const_f(2),
|
||||||
';': w_semi,
|
';': w_semi,
|
||||||
':': w_colon,
|
':': w_colon,
|
||||||
|
'->list': w_list,
|
||||||
|
'[': w_startlist,
|
||||||
|
']': w_endlist,
|
||||||
|
'{': w_startmap,
|
||||||
|
'}': w_endmap,
|
||||||
'+': w_add,
|
'+': w_add,
|
||||||
'+': w_add,
|
'+': w_add,
|
||||||
'-': w_sub,
|
'-': w_sub,
|
||||||
|
@ -43,22 +47,22 @@ class Forth:
|
||||||
'<=': w_le,
|
'<=': w_le,
|
||||||
'>=': w_ge,
|
'>=': w_ge,
|
||||||
'=': w_eq,
|
'=': w_eq,
|
||||||
'dup': w_dup,
|
'.': w_dot}
|
||||||
'swap': w_swap,
|
|
||||||
'.': w_dot,
|
self.import_from_module(words, 'w_')
|
||||||
'nl': w_nl,
|
|
||||||
'dump': w_dump,
|
|
||||||
'idump': w_idump,
|
|
||||||
'stack': w_stack,
|
|
||||||
'begin': w_begin,
|
|
||||||
'until': w_until,
|
|
||||||
'if': w_if,
|
|
||||||
'then': w_then}
|
|
||||||
|
|
||||||
self.compiler = None
|
self.compiler = None
|
||||||
if startup:
|
if startup:
|
||||||
execute_startup(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):
|
def defvar(self, name, value):
|
||||||
self.dictionary[name] = const_f(value)
|
self.dictionary[name] = const_f(value)
|
||||||
|
|
||||||
|
@ -81,6 +85,7 @@ class Forth:
|
||||||
self.compile_token(token)
|
self.compile_token(token)
|
||||||
|
|
||||||
def execute_file(self, fpath):
|
def execute_file(self, fpath):
|
||||||
|
old_source = self.dictionary.get('*source*', None)
|
||||||
self.defvar('*source*', fpath)
|
self.defvar('*source*', fpath)
|
||||||
with open(fpath) as f:
|
with open(fpath) as f:
|
||||||
line = f.readline()
|
line = f.readline()
|
||||||
|
@ -89,6 +94,7 @@ class Forth:
|
||||||
self.execute_tokens(tokens)
|
self.execute_tokens(tokens)
|
||||||
line = f.readline()
|
line = f.readline()
|
||||||
self.defvar('*source*', '')
|
self.defvar('*source*', '')
|
||||||
|
self.dictionary['*source*'] = old_source
|
||||||
|
|
||||||
def compile_token(self, token):
|
def compile_token(self, token):
|
||||||
if self.compiler.name == None:
|
if self.compiler.name == None:
|
||||||
|
|
|
@ -1,19 +1,62 @@
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from kernel import Forth
|
from kernel import Forth
|
||||||
from lex import tokenize
|
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__))
|
source_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
startup_file = f'{source_dir}/startup.sf'
|
startup_file = f'{source_dir}/startup.sf'
|
||||||
print(startup_file)
|
|
||||||
|
|
||||||
f = Forth()
|
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):
|
if os.path.exists(startup_file):
|
||||||
f.execute_file(startup_file)
|
f.execute_file(startup_file)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
p = f.evaluate_token('*prompt*')
|
p = f.evaluate_token('*prompt*')
|
||||||
line = input(p)
|
try:
|
||||||
|
line = input(p)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("<<interrupt>>")
|
||||||
|
line = ''
|
||||||
|
except EOFError:
|
||||||
|
break
|
||||||
|
|
||||||
tokens = tokenize(line)
|
tokens = tokenize(line)
|
||||||
f.execute_tokens(tokens)
|
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
|
"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 ;
|
: hello (simple greeting) "Hello" . nl ;
|
||||||
|
|
||||||
: >0 0 > ;
|
: >0 0 > ;
|
||||||
: <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
|
from compiler import Compiler
|
||||||
import importlib
|
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):
|
def execute_f(name, instructions):
|
||||||
# print("execute_f:", len(instructions))
|
# print("execute_f:", len(instructions))
|
||||||
|
@ -27,22 +67,98 @@ def ifnot_jump_f(n):
|
||||||
return 1
|
return 1
|
||||||
return ifnot_jump
|
return ifnot_jump
|
||||||
|
|
||||||
def const_f(value):
|
def jump_f(n):
|
||||||
def x(f):
|
def do_jump(forth):
|
||||||
f.stack.push(value)
|
return n
|
||||||
return 1
|
return do_jump
|
||||||
return x
|
|
||||||
|
|
||||||
def w_import(f):
|
def w_import(f):
|
||||||
name = f.stack.pop()
|
name = f.stack.pop()
|
||||||
m = importlib.import_module(name)
|
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):
|
def w_def(f):
|
||||||
value = f.stack.pop()
|
value = f.stack.pop()
|
||||||
name = f.stack.pop()
|
name = f.stack.pop()
|
||||||
f.defvar(name, value)
|
f.defvar(name, value)
|
||||||
print('name', name, 'value', value)
|
print('name', name, 'value', value)
|
||||||
|
return 1
|
||||||
|
|
||||||
def w_gt(f):
|
def w_gt(f):
|
||||||
a = f.stack.pop()
|
a = f.stack.pop()
|
||||||
|
@ -108,6 +224,19 @@ def w_dup(f):
|
||||||
f.stack.push(x)
|
f.stack.push(x)
|
||||||
return 1
|
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):
|
def w_swap(f):
|
||||||
a = f.stack.pop()
|
a = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
|
@ -143,6 +272,7 @@ def w_if(forth):
|
||||||
print("w_if")
|
print("w_if")
|
||||||
compiler = forth.compiler
|
compiler = forth.compiler
|
||||||
compiler.push_offset()
|
compiler.push_offset()
|
||||||
|
compiler.push_offset()
|
||||||
compiler.add_instruction(w_should_not_happen)
|
compiler.add_instruction(w_should_not_happen)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@ -150,14 +280,31 @@ w_if.__dict__['immediate'] = True
|
||||||
|
|
||||||
def w_then(forth):
|
def w_then(forth):
|
||||||
compiler = forth.compiler
|
compiler = forth.compiler
|
||||||
|
else_offset = compiler.pop_offset()
|
||||||
if_offset = compiler.pop_offset()
|
if_offset = compiler.pop_offset()
|
||||||
end_offset = compiler.offset()
|
then_offset = compiler.offset()
|
||||||
delta = end_offset - if_offset
|
if else_offset == if_offset:
|
||||||
compiler.instructions[if_offset] = ifnot_jump_f(delta)
|
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
|
return 1
|
||||||
|
|
||||||
w_then.__dict__['immediate'] = True
|
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):
|
def w_do(forth):
|
||||||
print("w_do")
|
print("w_do")
|
||||||
compiler = forth.compiler
|
compiler = forth.compiler
|
||||||
|
@ -196,6 +343,7 @@ def w_until(forth):
|
||||||
|
|
||||||
w_until.__dict__['immediate'] = True
|
w_until.__dict__['immediate'] = True
|
||||||
|
|
||||||
|
|
||||||
def w_dump(f):
|
def w_dump(f):
|
||||||
f.dump()
|
f.dump()
|
||||||
return 1
|
return 1
|
||||||
|
|
Loading…
Reference in a new issue