Fairly complete rewrite of VM. Now supports composite words with { w1 w2...}. Rewrote :, if, ifelse and while to use composite words. Namespaces now have Clojure like vars for indirection and also support inline words. Words defined in Python now use Python function decorators to add Sallyforth related metadata.

This commit is contained in:
Russ Olsen 2020-05-09 10:47:47 -04:00
parent ed5ccfd261
commit 9e9573b3dd
21 changed files with 728 additions and 1126 deletions

88
sallyforth/0.sf Normal file
View file

@ -0,0 +1,88 @@
"Hello from 0.sf" p
\ Pull in libs.
"builtins" load
"time" load
"math" load
"sys" load
"os" load
"os.path" load
"io" load
"time" load
'builtins import
'time import
\ Basic aliases
: -- { 1 - }
: ++ { 1 + }
: =0 { 0 = }
: pos? { 0 > }
: neg? { 0 < }
: zero? { 0 = }
: ->inline { *last-word* inline }
\ List making.
unique 'list-marker =!
: [ list-marker
: ] { list-marker [list] }
: [] { [ ] }
unique 'map-marker =!
: {{ map-marker
: }} { map-marker [list] list->map }
: {{}} { {{ }} }
\ Spelunk thru objects and properties.
: <. [
: .> { ] @@ }
: $? swap
\ Set the interactive prompt.
: *prompt* "sallySh> "
\ Function calling.
: !!0 { [] swap !! }
: !!1 { swap 1 ->list swap !! }
: !!2 { mbt 2 ->list swap !! }
: getattr ( obj attr -- attr-value ) {
swap 2 ->list builtins/getattr !!
}
: setattr ( obj attr value -- ) {
bmt 3 ->list builtins/setattr
}
: .!! (obj args method-name -- result) {
tbm getattr !!
}
\ Handy utilities
: str { builtins/str !!1 }
: type { builtins/type !!1 }
: callable? { builtins/callable !!1 }
: sleep { time/sleep !!1 drop }
: ctime { time/ctime !!0 }
: assert ( bool msg -- ) {
dup
p
swap
ifelse
{ drop "OK " p }
{ builtins/AssertionError !!1 raise }
}
\ Other startup files.
*sallyforth-dir* "/" "io.sf" + + source
*sallyforth-dir* "/" "list.sf" + + source
*sallyforth-dir* "/" "string.sf" + + source

View file

@ -1,2 +0,0 @@
class Arglist(list):
pass

View file

@ -1,161 +1,24 @@
from inspect import isfunction, isbuiltin
import tokenstream as ts
from wrappers import noop
from util import word
from unique import Unique
import importlib
import os
from compiler import Compiler
from arglist import Arglist
from operator_words import w_not
from pprint import pprint
def const_f(value):
def x(f, i):
#print("const f, pushing", value)
f.stack.push(value)
return i + 1
return x
@word('raise')
def w_raise(f):
ex = f.stack.pop()
raise ex
def native_function_handler(func):
def handle(forth, i):
args = forth.stack.pop()
#print(f"Native fun, calling {func}({args})")
result = func(*args)
#print(f'Result: {result}')
forth.stack.push(result)
#print("pushed result")
return i + 1
return handle
@word(immediate=True)
def readtoken(f):
t = f.stream.get_token()
def push_token(xforth):
xforth.stack.push(t)
return push_token
def import_native_module(forth, m, alias=None, excludes=[]):
if not alias:
alias = m.__name__
alias = alias.replace(".", "/")
print(m, alias)
raw_names = dir(m)
names = [x for x in raw_names if x not in excludes]
for name in names:
localname = f'{alias}/{name}'
val = getattr(m, name)
print("setting", localname)
forth.namespace.set(localname, const_f(val))
def w_eval(f, i):
token = f.stack.pop()
f.evaluate_string(token)
return i+1
def w_execute(f, i):
token = f.stack.pop()
f.execute_string(token)
return i+1
def w_no_op(f, i):
return i+1
def w_forth(f, i):
f.stack.push(f)
return i+1
def w_current_ns(f, i):
f.stack.push(f.namespace)
return i + 1
def w_ns(f, i):
name = f.stack.pop()
if name in f.namespaces:
f.namespace = f.namespaces[name]
else:
new_ns = f.make_namespace(name, {}, [f.forth_ns])
f.namespace = new_ns
return i + 1
def w_resolve(f, i):
token = f.stack.pop()
print("token", token)
resolved = f.resolve_token(token)
print("resovled:", resolved)
f.stack.push(resolved)
return i + 1
def w_alias(f, i):
new_name = f.stack.pop()
old_name = f.stack.pop()
f.namespace.alias(new_name, old_name)
return i + 1
def w_require(f, i):
name = f.stack.pop()
m = importlib.import_module(name)
import_native_module(f, m, name)
return i + 1
def source(f, path):
old_source_f = f.namespace.get('*source*', None)
old_namespace = f.namespace
try:
f.execute_file(path)
finally:
f.namespace['*source*'] = old_source_f
f.namespace = old_namespace
def w_load(f, i):
path = f.stack.pop()
source(f, path)
return i + 1
def w_source(f, i):
path = f.stack.pop()
if os.path.isabs(path):
source(f, path)
return i+1
relative_dir = os.path.dirname(f.evaluate_string('*source*'))
relative_path = f'{relative_dir}/{path}'
if os.path.exists(relative_path):
source(f, relative_path)
return i+1
source(f, path)
return i+1
def execute_f(name, instructions):
#print('execute_f:', name, len(instructions))
def inner(forth, i, debug=False):
#print('inner f:', name)
#print('inner f:', len(instructions))
j = 0
while j >= 0:
#print(j, '=>', instructions[j])
new_j = instructions[j](forth, j)
if new_j == None:
print(f'Instruction {instructions[j]} None')
raise RuntimeError
j = new_j
return i + 1
return inner
def ifnot_jump_f(n):
def ifnot_jump(forth, i):
x = forth.stack.pop()
if not x:
return i+n
return i+1
return ifnot_jump
def jump_f(n):
def do_jump(forth, i):
return n+i
return do_jump
def w_recur(f, i):
return 0
def w_import(f, i):
name = f.stack.pop()
m = importlib.import_module(name)
f.namespace.set(name, const_f(m))
return i+1
def w_call(f, i):
@word("!!")
def w_call(f):
func = f.stack.pop()
args = f.stack.pop()
#print('f', f, 'args', args)
@ -164,177 +27,171 @@ def w_call(f, i):
except:
print(f'Error executing {func}({args})')
raise
# print('result', result)
#print('result', result)
f.stack.push(result)
return i+1
def w_kwcall(f, i):
func = f.stack.pop()
kws = f.stack.pop()
args = f.stack.pop()
print('f', f, 'args', args, 'kws', kws)
try:
result = func(*args, **kws)
except:
print(f'Error executing {func}{list(args)}{kws}')
raise
print('result', result)
f.stack.push(result)
return i+1
@word()
def unique(f):
f.stack.push(Unique())
def w_nl(f, i):
print()
return i+1
def w_return(f, i):
return -9999;
def w_colon(f, i):
f.compiler = Compiler()
def i_inline(f, i):
f.compiler.inline = True
def i_semi(forth, i):
forth.compiler.add_instruction(w_return)
name = forth.compiler.name
word_f = execute_f(name, forth.compiler.instructions)
entry = forth.defword(name, word_f)
entry.inline = forth.compiler.inline
entry.definition = forth.compiler.instructions
#print(name)
#for ins in entry.definition:
# print(ins)
forth.compiler = None
return i+1
def w_compiling(f, i):
return f.stack.push(f.compiling())
def i_readtoken(f, i):
kind, token = f.read_next_token()
if f.compiling():
compiler = f.compiler
compiler.add_instruction(const_f(token))
else:
f.stack.push(token)
return i+1
def w_immediate(f, i):
flag = f.stack.pop()
@word()
def load(f):
name = f.stack.pop()
print(f'name: {name} flag {flag}')
f.namespace[name].immediate = flag
return i+1
m = importlib.import_module(name)
f.set_constant(name, m)
def w_should_not_happen(forth, i):
print('Should not execute this word!')
raise ValueError
@word('import')
def w_import(f):
name = f.stack.pop()
m = importlib.import_module(name)
f.ns.import_native_module(m)
def i_if(forth, i):
#print('w_if')
compiler = forth.compiler
compiler.push_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+1
@word()
def lexicon(f):
name = f.stack.pop()
m = importlib.import_module(name)
f.ns.import_from_module(m)
def i_ifnot(forth, i):
compiler = forth.compiler
compiler.add_instruction(w_not)
compiler.push_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+2
@word('source')
def w_source(f):
path = f.stack.pop()
f.eval_file(path)
def i_then(forth, i):
compiler = forth.compiler
else_offset = compiler.pop_offset()
if_offset = compiler.pop_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 i+1
@word('alias')
def w_alias(f):
new_name = f.stack.pop()
old_name = f.stack.pop()
f.alias(new_name, old_name)
def i_else(forth, i):
compiler = forth.compiler
compiler.pop_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+1
@word()
def rawdef(f):
name = f.stack.pop()
value = f.stack.pop()
f.set(name, value)
def i_do(forth, i):
#print('w_do')
compiler = forth.compiler
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+1
@word("=!")
def equal_bang(f):
name = f.stack.pop()
value = f.stack.pop()
f.set_constant(name, value)
def i_begin(forth, i):
compiler = forth.compiler
compiler.push_offset()
return i
def i_while(forth, i):
compiler = forth.compiler
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+1
@word("*prompt*")
def promptword(f):
f.stack.push(">> ")
def i_repeat(forth, i):
compiler = forth.compiler
while_offset = compiler.pop_offset()
begin_offset = compiler.pop_offset()
repeat_offset = compiler.offset()
begin_delta = begin_offset - repeat_offset
while_delta = repeat_offset - while_offset + 1
print("Begin delta", begin_delta)
print("while delta", while_delta)
compiler.instructions[while_offset] = ifnot_jump_f(while_delta)
compiler.add_instruction(jump_f(begin_delta))
return i+1
@word()
def lookup(f):
name = f.stack.pop()
f.stack.push(f.ns[name])
def w_marker1(f, i):
print("marker1")
return i+1
@word()
def forget(f):
name = f.stack.pop()
del f.ns[name]
def w_marker2(f, i):
print("marker3")
return i+1
@word()
def p(f):
print(f.stack.pop())
def w_marker3(f, i):
print("marker3")
return i+1
def w_dump(f, i):
f.dump()
return i+1
def i_idump(f, i):
f.dump()
return i+1
def w_stack(f, i):
print("Stack:", end=' ')
for x in f.stack:
print(f'{repr(x)}', end=' ')
@word()
def nl(f):
print()
return i+1
def w_enlist(f, i):
# print("Enlist!")
x = f.stack.pop()
# print("Popped", x)
f.stack.push([x])
return i+1
def w_raise(f, i):
ex = f.stack.pop()
raise ex
return i+1
@word('.')
def dot(f):
print(f.stack.pop(), end='')
@word()
def splat(f):
l = f.stack.pop()
l.reverse()
for x in l:
f.stack.push(x)
@word()
def stack(f):
print(f.stack)
@word()
def ns(f):
print(f.ns.name)
print(f.ns.contents)
@word(':', True)
def colon(forth):
name = forth.stream.get_token().value
body = forth.compile_next()
forth.set(name, body)
forth.set_constant('*last-word*', name)
return noop
@word()
def inline(forth):
name = forth.stack.pop()
print("Word name:", name)
var = forth.ns[name]
print('var', var)
print('value', var.value)
print('value dict', var.value.__dict__)
var.value.forth_inline = True
print('moded value dict', var.value.__dict__)
@word()
def current_stream(forth):
forth.stack.push(forth.stream)
@word()
def debug(forth):
word = forth.stack.pop()
print("Word:", word)
var = forth.ns[word]
pprint(var)
pprint(var.value.__dict__)
@word()
def fresult(forth, f):
f(forth)
result = forth.stack.pop()
return result
@word('while', True)
def w_while(forth):
cond = forth.compile_next()
body = forth.compile_next()
def dowhile(xforth):
b = fresult(xforth, cond)
while b:
body(xforth)
b = fresult(xforth, cond)
dowhile.forth_inline = False
dowhile.forth_primitive = True
dowhile.forth_immediate = False
return dowhile
@word('if', True)
def w_if(forth):
compiled = forth.compile_next()
print("compiled", compiled)
def doif(forth):
value = forth.stack.pop()
if value:
compiled(forth)
doif.forth_inline = False
doif.forth_primitive = True
doif.forth_immediate = False
return doif
@word('ifelse', True)
def ifelse(forth):
compiled_true = forth.compile_next()
compiled_false = forth.compile_next()
def doif(forth):
value = forth.stack.pop()
if value:
compiled_true(forth)
else:
compiled_false(forth)
doif.forth_inline = False
doif.forth_primitive = True
doif.forth_immediate = False
return doif

View file

@ -1,35 +1,63 @@
from stack import Stack
from tokenstream import Token
from wrappers import value_f, inner_f, ref_f
class Compiler:
def __init__(self, name=None):
self.name = name
self.instructions = []
self.offsets = Stack()
self.inline = False
LBrace = Token('word', '{')
RBrace = Token('word', '}')
def add_instruction(self, ins):
self.instructions.append(ins)
def compile_word(forth, w):
name = w.value
var = forth.ns[name]
value = var.value
def add_instructions(self, instructions):
self.instructions.extend(instructions)
if value.forth_immediate:
return value(forth)
elif var.dynamic:
return ref_f(var)
else:
return value
def offset(self):
return len(self.instructions)
def compile_token(forth, t):
if t.kind in ['number', 'string', 'keyword']:
f = value_f(t.value)
elif t.kind == 'word':
f = compile_word(forth, t)
else:
print(f'{n}??')
raise ValueError()
return f
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 compile_value(contents, v):
#print("compiling", v, v.__dict__)
if v.forth_inline and v.forth_contents:
contents.extend(v.forth_contents)
else:
contents.append(v)
return contents
def pop_offset(self):
return self.offsets.pop()
def compile_next(forth, stream, current_token=None):
if current_token:
t = current_token
else:
t = stream.get_token()
def _str__(self):
result = f'Compiler {name}'
for i in self.instructions:
result += str(i)
result += ' '
return result
if t == None:
return None
if t != LBrace:
return compile_token(forth, t)
contents = []
t = stream.get_token()
while t != RBrace:
compile_value(contents, compile_next(forth, stream, t))
t = stream.get_token()
f = inner_f(contents)
return f
def eval_stream(forth, stream):
t = stream.get_token()
while t:
compiled = compile_next(forth, stream, t)
#print(f"*** compiled {t} => {compiled}")
compiled(forth)
t = stream.get_token()

View file

@ -1,48 +1,8 @@
from util import word, get_attribute
from unique import Unique
from arglist import Arglist
def w_unique(f, ip): # pushes a uique object.
f.stack.push(Unique())
return ip+1
def w_map(f, ip):
word = f.stack.pop()
l = f.stack.pop()
word_f = f.namespace.get(word, None).get_ivalue()
result = []
for item in l:
f.stack.push(item)
word_f(f, 0)
result.append(f.stack.pop())
f.stack.push(result)
return ip+1
def w_reduce(f, ip):
l = f.stack.pop()
word = f.stack.pop()
word_f = f.namespace.get(word, None).get_ivalue()
if len(l) <= 0:
f.stack.push(None)
elif len(l) == 1:
f.stack.push(l[0])
else:
result = l[0]
l = l[1::-1]
for item in l:
f.stack.push(result)
f.stack.push(item)
word_f(f, 0)
result = f.stack.pop()
f.stack.push(result)
return ip+1
def w_bounded_list(f, ip):
@word('[list]')
def w_bounded_list(f):
"""Create a list from delimted values on the stack.
[list]
(marker a b c marker -- [a b c]
@ -59,22 +19,54 @@ def w_bounded_list(f, ip):
x = f.stack.pop()
l.reverse()
f.stack.push(l)
return ip+1
def w_to_arglist(f, ip):
l = f.stack.pop()
f.stack.push(Arglist(l))
return ip+1
def w_list(f, ip): # ->list
@word('->list')
def w_list(f):
n = f.stack.pop()
l = []
for i in range(n):
l.append(f.stack.pop())
f.stack.push(l)
return ip+1
def w_thread(f, i): # @@
@word()
def w_map(f):
word = f.stack.pop()
l = f.stack.pop()
word_f = f.lookup(word)
print(word_f)
result = []
for item in l:
f.stack.push(item)
word_f(f)
result.append(f.stack.pop())
f.stack.push(result)
@word()
def w_reduce(f):
l = f.stack.pop()
word = f.stack.pop()
print(f'L: {l} word {word}')
word_f = f.lookup(word)
if len(l) <= 0:
f.stack.push(None)
elif len(l) == 1:
f.stack.push(l[0])
else:
result = l[0]
l = l[1::]
for item in l:
f.stack.push(result)
f.stack.push(item)
word_f(f)
result = f.stack.pop()
f.stack.push(result)
@word('@@')
def w_thread(f):
contents = f.stack.pop()
print("Contents:", contents)
result = contents[0]
@ -82,79 +74,28 @@ def w_thread(f, i): # @@
print("Result:", result)
if isinstance(field, str) and hasattr(result, field):
result = getattr(result, field) # result.field
elif isinstance(field, Arglist):
result = result(*field) # result(*field)
else:
result = result[field] # result[field]
f.stack.push(result)
return i+1
ListMarker = object()
def w_startlist(f, i): # [
f.stack.push(ListMarker)
return i+1
def w_endlist(f, i): # ]
l = []
x = f.stack.pop()
while x != ListMarker:
l.append(x)
x = f.stack.pop()
l.reverse()
f.stack.push(l)
return i+1
MapMarker = object()
def w_startmap(f, i): # {
f.stack.push(MapMarker)
return i+1
def w_endmap(f, ip): # }
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 i+1
l.reverse()
result = {}
for i in range(0, len(l), 2):
result[l[i]] = l[i+1]
f.stack.push(result)
return ip+1
def w_list_to_map(f, ip): # list->map
@word('list->map')
def w_list_to_map(f): # list->map
l = f.stack.pop()
result = {}
for i in range(0, len(l), 2):
result[l[i]] = l[i+1]
f.stack.push(result)
return ip+1
def w_get(f, i):
@word()
def w_get(f):
name = f.stack.pop()
m = f.stack.pop()
result = m[name]
f.stack.push(result)
return i+1
def w_getattribute(f, i):
@word()
def w_getattribute(f):
name = f.stack.pop()
x = f.stack.pop()
result = x.__getattribute__(name)
f.stack.push(result)
return i+1
def w_def(f, i):
value = f.stack.pop()
name = f.stack.pop()
f.defvar(name, value)
# print('name', name, 'value', value)
return i+1

View file

@ -1,52 +0,0 @@
'*prompt* "sallySh> " def
: child-run-prog (argv pid -- <<exit>>)
drop \ Get rid of 0 pid.
execvp \ Run the program
;
: parent-wait (cmd pid -- exit-status)
"parent" p
waitpid
"Child status:" p p
drop
;
: run-prog (argv -- status)
'child-run-prog 'parent-wait fork
;
'cmd-marker unique def
: a cmd-marker ;
: z
cmd-marker bounded_list
stack
run-prog
;
'os import
: expandvars #os/path/expandvars !!1 ;
: expanduser #os/path/expanduser !!1 ;
: expand expanduser expandvars ;
: tokenize <. $? 'split .> " " swap !!1 ;
: prompt-and-run ( -- prog-status)
">> " read-line
dup "q" =
if
"Quit!" p
drop
else
tokenize
'expand map
run-prog
recur
then
;
: % "Toggle" p ;

View file

@ -1,12 +1,8 @@
: open builtins/open !!1 ;
: close <. $? 'close .> !!0 drop ;
: open { builtins/open !!1 }
: close { <. $? 'close .> !!0 drop }
: read-file (path -- contents) open dup <. $? 'read .> !!0 swap close ;
: read-lines (path -- contents) open dup <. $? 'readlines .> !!0 swap close ;
: read-file (path -- contents) { open dup <. $? 'read .> !!0 swap close }
: read-lines (path -- contents) { open dup <. $? 'readlines .> !!0 swap close }
: read-line (prompt -- input-line)
builtins/input !!1
;
\ : read-next nexttoken second ;
: read-line (prompt -- input-line) { builtins/input !!1 }

View file

@ -1,212 +1,82 @@
import sys
from os import path
import basic_words, data_words, operator_words, stack_words, os_words
from basic_words import const_f, w_enlist
import tokenstream as ts
from kword import Keyword
import os
from stack import Stack
from namespace import Namespace
import basic_words
import stack_words
import operator_words
import data_words
import tokenstream as ts
import compiler
from wrappers import value_f
class Forth:
def __init__(self, startup=None):
self.streams = Stack()
def __init__(self):
self.stack = Stack()
self.namespaces = {}
self.stream = None
self.ns = Namespace('core')
self.set_constant('forth', self)
self.set_constant('nil', None)
self.set_constant('true', True)
self.set_constant('false', False)
self.set_constant('*source*', '<<input>>')
self.set_constant('*sallyforth-dir*',
os.path.dirname(os.path.abspath(__file__)))
self.ns.import_from_module(basic_words)
self.ns.import_from_module(stack_words)
self.ns.import_from_module(operator_words)
self.ns.import_from_module(data_words)
self.forth_ns = self.make_namespace('forth')
self.namespace = self.forth_ns
user_ns = self.make_namespace('user', {}, [self.forth_ns])
def set_constant(self, name, value):
return self.ns.set(name, value_f(value))
self.defword('*prompt*', const_f('SallyForth>> '))
self.defword('*source*', const_f(__file__))
self.defword('true', const_f(True))
self.defword('false', const_f(False))
self.defword('None', const_f(None))
self.defword('0', const_f(0))
self.defword('1', const_f(1))
self.defword('2', const_f(2))
def set(self, name, fvalue):
return self.ns.set(name, fvalue)
self.forth_ns.import_from_module(basic_words)
self.forth_ns.import_from_module(data_words)
self.forth_ns.import_from_module(operator_words)
self.forth_ns.import_from_module(stack_words)
self.forth_ns.import_from_module(os_words)
self.namespace.alias("*execute-command*", "execute")
def get(self, name, def_value=None):
if name in self.ns:
return self.ns[name]
return def_value
self.compiler = None
def alias(self, new_name, old_name):
self.ns.alias(new_name, old_name)
self.defvar("argv", sys.argv[1::])
def compile_next(self, current_token=None):
return compiler.compile_next(self, self.stream, current_token)
if startup:
self.execute_file(startup)
def eval_stream(self, stream):
old_stream = self.stream
self.stream = stream
compiler.eval_stream(self, stream)
self.stream = old_stream
self.namespace = user_ns
def eval_file(self, path):
old_source = self.ns['*source*']
with open(path) as f:
fns = ts.file_token_stream(f)
return self.eval_stream(fns)
self.ns['*source*'] = old_source
def defword(self, name, value):
return self.namespace.set(name, value)
def eval_string(self, s):
self.eval_stream(ts.string_token_stream(s))
def defvar(self, name, value):
return self.defword(name, const_f(value))
def compiling(self):
return self.compiler
def _compile_token(self, token):
#print(f"compile: {self.compiler.name}: {token}")
if self.compiler.name == None:
#print(f'Compiling {token}')
self.compiler.name = token.value
return
if token.isnumber() or token.isstring():
self.compiler.add_instruction(const_f(token.value))
return
if token.iskeyword():
self.compiler.add_instruction(Keyword(token.value))
return
if token.value not in self.namespace:
print(f'[{token}]?? Compile of [{self.compiler.name}] terminated.')
self.compiler = None
return
entry = self.namespace[token.value]
if entry.immediate:
value = entry.get_ivalue()
value(self, 0)
elif entry.inline:
self.compiler.add_instructions(entry.definition[slice(0,-1)])
else:
value = entry.get_cvalue()
self.compiler.add_instruction(value)
def _eval_token(self, token):
#print(f'***Eval token {token}')
if token == None:
print(f'{token}?')
elif token.isnumber() or token.isstring():
self.stack.push(token.value)
elif token.iskeyword():
self.stack.push(Keyword(token.value))
return
elif token.value not in self.namespace:
print(f"{token.value}??")
else:
entry = self.namespace[token.value]
f = entry.get_ivalue()
f(self, 0)
def execute_token(self, token):
#print(f'execute_token: {token}')
expanded_tokens = self.macro_expand_token(token)
#print(expanded_tokens)
for expanded in expanded_tokens:
if not self.compiling():
self._eval_token(expanded)
else:
self._compile_token(expanded)
def execute_current_stream(self):
s = self.streams.peek()
#print("exec current s:", s)
token = s.get_token()
while token:
#print("Exec:", token)
self.execute_token(token)
token = s.get_token()
self.streams.pop()
def execute_token_stream(self, s):
#print("exec token stream:", s)
self.streams.push(s)
self.execute_current_stream()
def execute_string(self, s):
token_stream = ts.string_token_stream(s)
return self.execute_token_stream(token_stream)
def resolve_token(self, s):
token_stream = ts.string_token_stream(s)
token = token_stream.get_token()
print("token", token)
if token.isstring():
return token.value
elif token.isnumber():
return token.value
elif token.isword():
entry = self.namespace[token.value]
return entry.get_ivalue()
else:
return None
def evaluate_string(self, s):
self.execute_string(s)
def eval_string_r(self, s):
self.eval_string(s)
return self.stack.pop()
def read_next_token(self):
s = self.streams.peek()
return s.get_token()
def lookup(self, name):
return self.ns[name]
def py_evaluate(self, s, *args):
#print(f'Evaluate: token [{token}] args <<{args}>>')
rargs = list(args)
rargs.reverse()
if rargs:
for a in rargs:
#print("pushing", a);
self.stack.push(a)
#print(f'Before eval stack is {str(self.stack)}')
return self.evaluate_string(s)
def macro_expand_token(self, token):
if not token.isword():
return [token]
elif len(token.value) <= 1:
return [token]
elif token.value[0] != '#':
return [token]
print("Expanding token:", token)
tag = token.value[1:]
parts = tag.split('.')
print("Parts", parts)
result = [ ts.Token('word', '<.'), ts.Token('word', parts[0]) ]
for part in parts[1::]:
result.append(ts.Token('string', part))
result.append(ts.Token('word', '.>'))
print(result)
return result
def set_ns(self, ns_name):
if ns_name in self.namespaces:
self.namespace = self.namespaces[ns_name]
else:
raise ValueError(f'No such namespace: {ns_name}')
def make_namespace(self, ns_name, initial_defs={}, refers=[]):
#print(f'New namespace {ns_name} {refers}')
result = Namespace(ns_name, initial_defs, refers)
self.namespaces[ns_name] = result
#print(f'Returning {result}')
return result
def execute_file(self, fpath):
old_source = self.namespace.get('*source*', None)
old_namespace = self.namespace
self.defvar('*source*', fpath)
with open(fpath) as f:
fts = ts.file_token_stream(f)
self.execute_token_stream(fts)
self.namespace['*source*'] = old_source
self.namespace = old_namespace
def dump(self):
print('Forth:', self)
print('Stack:', self.stack)
print('Dictionary:', self.namespace)
print('Compiler:', self.compiler)
if __name__ == "__main__":
x = 0
def pmt():
global x
x += 1
return f'Yes{x}>> '
pis = ts.PromptInputStream(pmt)
tstream = ts.TokenStream(pis.getc)
forth = Forth()
forth.eval_stream(tstream)

View file

@ -1,47 +1,48 @@
\ Index into the x'th item.
: [x] (col key -- value) 1 ->list '__getitem__ .!! ;
: first (list -- first-item) 0 [x] ;
: second (list -- second-item) 1 [x] ;
: third (list -- third-item) 2 [x] ;
: fourth (list -- fourth-item) 3 [x] ;
: [x] (col key -- value) { 1 ->list '__getitem__ .!! }
: last (list -- last-item) -1 [x] ;
: first (list -- first-item) { 0 [x] }
: second (list -- second-item) { 1 [x] }
: third (list -- third-item) { 2 [x] }
: fourth (list -- fourth-item) { 3 [x] }
: slice (start stop -- slice-obj)
: last (list -- last-item) { -1 [x] }
: slice (start stop -- slice-obj) {
swap
2 ->list
builtins/slice
!!
;
}
: take (n list -- first-n-items)
: take (n list -- first-n-items) {
swap 0 swap slice \ Make the 0..n slice.
[x] \ Do a[0..n].
;
}
: skip (n list -- all-but-first-n-items)
: skip (n list -- all-but-first-n-items) {
swap nil slice \ Make the n..None slice.
[x]
;
}
: n-of (n x -- list-of-x-repeated-n-times)
: n-of (n x -- list-of-x-repeated-n-times) {
1 ->list *
;
}
: len builtins/len !!1 ;
: len { builtins/len !!1 }
: empty? len zero? ;
: empty? { len zero? }
: rest (list -- all-but-first) 1 swap skip ;
: rrest (list -- rest-of-rest) rest rest ;
: rrrest (list -- all-but-first) rest rest rest ;
: rest (list -- all-but-first) { 1 swap skip }
: rrest (list -- rest-of-rest) { rest rest }
: rrrest (list -- all-but-first) { rest rest rest }
: ffirst (list -- first-of-first) first first ;
: fffirst (list -- fff-irst) first first first ;
: ffirst (list -- first-of-first) { first first }
: fffirst (list -- fff-irst) { first first first }
: append (x list -- list-with-x-appended)
: append (x list -- list-with-x-appended) {
dup tbm
<. $? 'append .> !!1
drop
;
}

View file

@ -1,38 +1,26 @@
class Entry:
def __init__(self, name, value, immed):
from util import get_attribute
from wrappers import value_f
class Var:
def __init__(self, name, value, dynamic=True):
self.name = name
self.value = value
self.immediate = immed
self.inline = False
self.definition = None
def get_ivalue(self):
return self.value
def get_cvalue(self):
return self.value
self.dynamic = dynamic
def __str__(self):
result = f'Entry {self.name} {self.immediate} {self.inline}\n'
for x in self.definition:
result += f'{x}\n'
return result
return f'[[[[Var({self.name}/{self.dynamic}::{self.value})]]]'
def __repr__(self):
return str(self)
class Namespace:
def __init__(self, name, initial_contents={}, refers=[]):
def __init__(self, name):
self.contents = {}
self.name = name
self.contents = initial_contents.copy()
self.refers = refers.copy()
def alias(self, new_name, existing_name):
self.contents[new_name] = self.contents[existing_name]
def refer(self, ns):
"""
Add the supplied namespace to the refers list.
"""
self.refers.append(ns)
def import_from_module(self, m):
"""
Import all of the word defining functions in
@ -41,82 +29,55 @@ class Namespace:
"""
names = dir(m)
for name in names:
if name.startswith("w_"):
word_name = name[2::]
#print(f'Setting {word_name} to false')
self.set(word_name, getattr(m, name))
elif name.startswith("i_"):
word_name = name[2::]
#print(f'Setting {word_name} to true')
self.set(word_name, getattr(m, name), immediate=True)
value = getattr(m, name)
if get_attribute(value, 'forth_word'):
forth_name = value.forth_name or name
var = self.set(forth_name, value, False)
var.immediate = value.forth_immediate
#print(var)
if var.immediate:
print(name, 'immediate')
def set(self, name, value, cvalue=None, immediate=False):
if name not in self.contents:
entry = Entry(name, value, immediate)
def import_native_module(self, m, alias=None):
if not alias:
alias = m.__name__
alias = alias.replace(".", "/")
print(m, alias)
names = dir(m)
for name in names:
localname = f'{alias}/{name}'
val = getattr(m, name)
#print("setting", localname)
var = self.set(localname, value_f(val), False)
def set(self, key, value, dynamic=True):
if key not in self.contents:
var = Var(key, value, dynamic)
self.contents[key] = var
else:
entry = self[name]
entry.value = value
entry.cvalue = cvalue
entry.immediate = immediate
self.contents[name] = entry
return entry
var = self.contents[key]
var.value = value
var.dynamic = dynamic
return var
def keys(self):
return self.contents.keys()
def all_keys(self):
result = set(self.contents.keys())
for r in self.refers:
result = result.union(set(r.contents.keys()))
return result
def get(self, key, default):
if not self.__contains__(key):
return default
return self[key]
def __contains__(self, key):
#print(f'Namespace contains {key}')
if self.contents.__contains__(key):
#print(self.contents[key])
return True
for r in self.refers:
if r.__contains__(key):
return True
return False
def local_contains(self, key):
return self.contents.__contains__(key)
return self.contents.__contains(key)
def __delattr__(self, key):
return self.contents.__delattr__(key)
def __setitem__(self, key, x):
self.contents[key] = x
return self.set(key, x)
def __iter__(self):
return self.contents.__iter__()
def __getitem__(self, key):
#print("get item", key, self.name)
if key in self.contents:
return self.contents[key]
# print("not in local ns")
for imp in self.refers:
# print("trying ", imp)
if key in imp:
return imp[key]
# print("not found")
raise KeyError(key)
return self.contents.__getitem__(key)
def __str__(self):
return f'Namespace({self.name})'
if __name__ == '__main__':
print("main program")
x = Namespace('x', {'a': 1, 'b': 2})
print(x['a'])
y = Namespace('y', {'c': 3, 'd': 4})
print(y['c'])
y.refer(x)
print(y['a'])

View file

@ -1,67 +1,69 @@
def w_gt(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b > a)
return i+1
from util import word
def w_lt(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b < a)
return i+1
@word('>')
def gt(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b > a)
def w_eq(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(a==b)
return i+1
@word('<')
def lt(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b < a)
def w_le(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b<=a)
return i+1
@word('=')
def eq(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(a==b)
def w_ge(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b>=a)
return i+1
@word('<=')
def le(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b<=a)
def w_add(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b+a)
return i+1
@word('>=')
def ge(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b>=a)
def w_mul(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b*a)
return i+1
@word('+')
def add(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b+a)
def w_sub(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b-a)
return i+1
@word('*')
def mul(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b*a)
def w_div(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(b/a)
return i+1
@word('-')
def sub(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b-a)
def w_and(f, i):
f.stack.push(f.stack.pop() and f.stack.pop())
return i+1
@word('/')
def div(forth):
a = forth.stack.pop()
b = forth.stack.pop()
forth.stack.push(b/a)
def w_or(f, i):
f.stack.push(f.stack.pop() or f.stack.pop())
return i+1
@word('and')
def w_and(forth):
forth.stack.push(forth.stack.pop() and forth.stack.pop())
def w_not(f, i):
f.stack.push(not f.stack.pop())
return i+1
@word('or')
def w_or(forth):
forth.stack.push(forth.stack.pop() or forth.stack.pop())
@word('not')
def w_not(forth):
forth.stack.push(not forth.stack.pop())

View file

@ -1,7 +1,9 @@
import os
import sys
from util import word
def w_fork(f, i):
@word('fork')
def w_fork(f):
parent_word = f.stack.pop()
child_word = f.stack.pop()
parent_f = f.namespace.get(parent_word, None).get_ivalue()
@ -14,28 +16,26 @@ def w_fork(f, i):
else:
print("parent:", pid)
parent_f(f, 0)
return i+1
def w_execvp(f, i):
@word('execvp')
def w_execvp(f):
args = f.stack.pop()
path = args[0]
print(f"path {path} args: {args}")
os.execvp(path, args)
return i+1
def w_waitpid(f, i):
@word('waitpid')
def w_waitpid(f):
pid = f.stack.pop()
result = os.waitpid(pid, 0)
f.stack.push(result)
return i+1
def w_exit(f, i):
@word('exit')
def w_exit(f):
n = f.stack.pop()
sys.exit(n)
return i+1
def w_exit_bang(f, i):
@word('exit!')
def w_exit_bang(f):
n = f.stack.pop()
os._exit(n)
return i+1

View file

@ -14,7 +14,8 @@ class Completer:
def __init__(self, f):
self.f = f
def complete(self, prefix, index):
self.matching_words = [w for w in self.f.namespace.all_keys() if w.startswith(prefix) ]
self.matching_words = \
[w for w in self.f.ns.keys() if w.startswith(prefix)]
try:
return self.matching_words[index]
except IndexError:
@ -27,6 +28,7 @@ def setup_readline(history_path, f):
except FileNotFoundError:
pass
readline.parse_and_bind("tab: complete")
readline.set_completer_delims(' \t\n()[{]}\\|;:\'",')
readline.set_completer(completer.complete)
def save_history():
readline.write_history_file(history_path)
@ -34,27 +36,24 @@ def setup_readline(history_path, f):
def setup_forth():
source_dir = os.path.dirname(os.path.abspath(__file__))
startup_file = f'{source_dir}/startup.sf'
startup_file = f'{source_dir}/0.sf'
f = Forth()
if os.path.exists(startup_file):
f = Forth(startup_file)
else:
f = Forth()
f.eval_file(startup_file)
return f
def repl(f):
while True:
try:
prompt = f.evaluate_string('*prompt*')
prompt = f.eval_string_r('*prompt*')
try:
line = input(prompt)
line += "\n"
except EOFError:
return
try:
f.stack.push(line)
f.execute_string("*execute-command*")
#f.execute_string(line)
f.eval_string(line)
except:
traceback.print_exc()
except KeyboardInterrupt:

View file

@ -1,43 +1,33 @@
class Stack:
def __init__(self):
self.top = -1
self.stack = 100 * [None]
self.stack = []
def push(self, x):
#print("stack push", x)
self.top += 1
self.stack[self.top] = x
self.stack.append(x)
return x
def pop(self):
result = self.stack[self.top]
#print("stack pop", result)
self.top -= 1
if self.top < -1:
print("stack overpop")
self.top = -1;
raise ValueError("Stack underflow")
return result
return self.stack.pop()
def __iter__(self):
for i in range(0, self.top+1):
yield self.stack[i]
for x in self.stack:
yield x
def depth(self):
return self.top + 1
return len(self.stack)
def empty(self):
return self.top == -1
return len(self.stack) == 0
def peek(self):
return self.stack[self.top]
return self.stack[-1]
def reset(self):
self.top = -1
self.stack = []
def __str__(self):
result = ''
for i in range(self.top + 1):
result += str(self.stack[i])
for x in self.stack:
result += str(x)
result += ' '
return result

View file

@ -1,101 +1,83 @@
def w_px(f, i):
args = f.stack.pop()
name = f.stack.pop()
m = f.stack.pop()
func = m.__dict__[name]
result = func(*args)
f.stack.push(result)
return i+1
from util import word
def w_reset(f, i):
a = f.stack.reset()
return i+1
@word()
def stackdepth(forth):
forth.stack.push(forth.stack.depth())
def w_dot(f, i):
@word()
def reset(forth):
forth.stack.reset()
@word()
def drop(forth):
forth.stack.pop()
@word()
def dup(forth):
a = forth.stack.peek()
forth.stack.push(a)
@word()
def swap(f):
a = f.stack.pop()
print(a, end='')
return i+1
b = f.stack.pop()
f.stack.push(a)
f.stack.push(b)
@word()
def tmb(f): # A noop
pass
def w_stackdepth(f, i):
d = f.stack.depth()
f.stack.push(d)
def w_splat(f, i):
l = f.stack.pop()
l.reverse()
for x in l:
f.stack.push(x)
return i+1
def w_dup(f, i):
x = f.stack.peek()
f.stack.push(x)
return i+1
def w_tmb(f, i): # A noop
return i+1
def w_tbm(f, i):
@word()
def tbm(f):
t = f.stack.pop()
m = f.stack.pop()
b = f.stack.pop()
f.stack.push(m)
f.stack.push(b)
f.stack.push(t)
return i+1
def w_bmt(f, i):
@word()
def bmt(f):
t = f.stack.pop()
m = f.stack.pop()
b = f.stack.pop()
f.stack.push(t)
f.stack.push(m)
f.stack.push(b)
return i+1
def w_btm(f, i):
@word()
def btm(f):
t = f.stack.pop()
m = f.stack.pop()
b = f.stack.pop()
f.stack.push(m)
f.stack.push(t)
f.stack.push(b)
return i+1
def w_mtb(f, i):
@word()
def mtb(f):
t = f.stack.pop()
m = f.stack.pop()
b = f.stack.pop()
f.stack.push(b)
f.stack.push(t)
f.stack.push(m)
return i+1
def w_mbt(f, i):
@word()
def mbt(f):
t = f.stack.pop()
m = f.stack.pop()
b = f.stack.pop()
f.stack.push(t)
f.stack.push(b)
f.stack.push(m)
return i+1
def w_rot(f, i):
@word()
def 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 i+1
def w_drop(f, i):
f.stack.pop()
return i+1
def w_swap(f, i):
a = f.stack.pop()
b = f.stack.pop()
f.stack.push(a)
f.stack.push(b)
return i+1

View file

@ -1,151 +0,0 @@
\ "Executing " dot *source* dot nl
\ Pull in libs.
"builtins" require
"time" require
"math" require
"sys" require
"os" require
"os.path" require
"io" require
"time" require
'builtins import
\ Basic aliases
'None 'nil alias
'dot '. alias
'colon ': alias
'semi '; alias
'thread '@@ alias
'exit_bang 'exit! alias
'bounded_list '[list] alias
'list '->list alias
'to_arglist '->arglist alias
'list_to_map 'list->map alias
'call '!! alias
'add '+ alias
'sub '- alias
'mul '* alias
'div '/ alias
'gt '> alias
'lt '< alias
'le '<= alias
'ge '>= alias
'eq '= alias
'current_ns '*ns* alias
: *prompt* "Sally> " ;
: *execute-command* execute ;
\ Make a list.
'list-marker unique def
: [ list-marker inline ;
: ] list-marker [list] inline ;
: [] ( -- <empty list>) [ ] inline ;
\ Look up attributes on a value.
: <. [ ;
: .> ] thread ;
: $? swap ;
\ Call native functions with various # arguments.
: !!0 [] swap !! inline ;
: !!1 swap 1 ->list swap !! inline ;
: !!2 swap 2 ->list swap !! inline ;
\ Make a map.
'map-marker unique def
: { map-marker inline ;
: } map-marker [list] list->map inline ;
: {} ( -- <empty map>) { } inline ;
\ Make a set.
'set-marker unique def
: {{ set-marker ;
: }}
set-marker [list] \ Turn elements into list
set-marker swap set-marker [list] \ Nest list in argument list
builtins/set !! \ Call set with 1 argument
inline
;
: {{}} ( -- <empty set>) {{ }} inline ;
: [: [ inline ;
: :] ] ->arglist inline ;
: str builtins/str !!1 ;
: type builtins/type !!1 ;
: type? (x class -- bool) swap type = ;
: ctime time/ctime !!0 ;
: sleep time/sleep !!1 drop ;
: callable? builtins/callable !!1 ;
: hello "Hello" . nl ;
: >0 0 > inline ;
: =0 0 = inline ;
: <1 1 < inline ;
: <0 0 < inline ;
: >1 1 > inline ;
: <1 1 < inline ;
: p . nl ;
: top dup p ;
: -- -1 + inline ;
: ++ 1 + inline ;
: *2 2 * inline ;
: pos? 0 > inline ;
: neg? 0 < inline ;
: zero? 0 = inline ;
: exists? os/path/exists !!1 ;
: source-if-exists
(path -- result-of-sourcing)
"SOURCE IF EXISTS" p
stack
dup
exists?
if source else drop then
;
: getattr ( obj attr -- attr-value ) swap 2 ->list builtins/getattr !! ;
: .!! (obj args method-name -- result) tbm getattr !! ;
: .!!0 (obj method-name -- result ) [] swap .!! ;
: .!!1 (obj arg method-name -- result ) swap 1 ->list swap .!! ;
: .!!2 (obj a1 a2 method-name -- result ) swap 2 ->list swap .!! ;
: .!!3 (obj a1 a2 a3 method-name -- result ) swap 3 ->list swap .!! ;
: assert ( bool msg -- )
p
if
"OK " p
else
#builtins/AssertionError !!1
raise
then
;
"string.sf" source
"list.sf" source
"io.sf" source
"init.sf" source-if-exists

View file

@ -1,5 +1,9 @@
: split (delimit str -- tokens) 2 ->list <. builtins/str 'split .> !! ;
: split (delimit str -- tokens) {
2 ->list
<. builtins/str 'split .>
!!
}
: dot-split (str -- tokens) "." swap split ;
: dot-split (str -- tokens) { "." swap split }

View file

@ -18,7 +18,6 @@ reset 1 2 3 reset stackdepth 0 = "Reset empties the stack." assert
2 3 * 6 = "2*3 = 6" assert
100 5 / 20 = "100 divided by 5 is 20." assert
1 >0 "One is > 0." assert
0 =0 "Zero is equal to itself." assert
1 =0 not "One is not =0." assert
@ -46,22 +45,23 @@ false false and not "F and F is F." assert
\ Secondary words
: push8 8 ; push8 8 = "A word can push a number." assert
: push8 8
push8 8 = "A word can push a number." assert
: push8-again push8 ;
: push8-again push8
push8-again 8 = "A word can call another word." assert
: push64 push8 push8 * ;
: push64 { push8 push8 * }
push64 64 = "A word can use primitive and sec words." assert
\ Logic
: 1-if-true if 1 then ;
: 1-if-true { if { 1 } }
reset true 1-if-true 1 = "True part of if fires." assert
reset false 1-if-true stackdepth 0 = "if does not fire on false." assert
: 1-or-2 if 1 else 2 then ;
: 1-or-2 { ifelse 1 2 }
reset true 1-or-2 1 = "True part of ifelse fires." assert
reset false 1-or-2 2 = "False part of ifelse fires." assert
@ -74,7 +74,7 @@ reset false 1-or-2 2 = "False part of ifelse fires." assert
\ Name lookup and calls
"12" <. builtins 'len .> !!1 2 = "Can use bracket dot notation." assert
"12" #builtins.len !!1 2 = "Can use sharp lookup notation." assert
"12" builtins/len !!1 2 = "Can use sharp lookup notation." assert
\ Lists
@ -94,10 +94,10 @@ reset false 1-or-2 2 = "False part of ifelse fires." assert
\ Loop
: test-while ( n -- ) -999 swap begin dup zero? while -- repeat -888 ;
: test-while ( n -- ) { -999 swap while { dup zero? } { -- } -888 }
5 test-while 3 ->list [ -999 0 -888 ] "While loop works" assert
: zero-trip-while begin false while "Should not get here." repeat ;
: zero-trip-while { while { false } { "Should not get here." } }
888 zero-trip-while 888 = "While should handle zero trip case." assert

View file

@ -14,6 +14,17 @@ class Token:
self.kind = kind
self.value = value
def __eq__(self, other):
if not isinstance(other, Token):
return False
return self.kind == other.kind and self.value == other.value
def __hash__(self):
return self.kind.__hash__() + self.value.__hash__()
def isblock(self):
return self.kind == 'block'
def isstring(self):
return self.kind == 'string'
@ -30,7 +41,7 @@ class Token:
return str(self)
def __str__(self):
return f'Token[{self.kind} => {self.value}]'
return f'Token {self.kind} => {self.value}'
class PromptInputStream:
@ -52,6 +63,7 @@ class PromptInputStream:
class TokenStream:
def __init__(self, read_f):
#print("Tokenstream", read_f)
self.read_f = read_f
def whitespace(self, ch):
@ -73,6 +85,9 @@ class TokenStream:
return Token('string', token)
if state in ['word']:
return Token('word', token)
if state == 'number':
return Token('number', token)
#print("x get returning NONE")
return None
elif state == 'start' and ch == ':':
token = ch
@ -101,7 +116,8 @@ class TokenStream:
token += ch
elif state == 'number' and self.whitespace(ch):
n = to_number(token)
if n:
if n != None:
#print("returning number", n)
return Token('number', n)
else:
return Token('word', token)
@ -111,13 +127,14 @@ class TokenStream:
return Token('string', token)
elif state == 'keyword' and self.whitespace(ch):
state = 'start'
if len(token) == 1:
if token in [':']:
return Token('word', token)
return Token('keyword', token)
elif state in ['word', 'dqstring', 'sqstring', 'number', 'keyword']:
token += ch
def file_token_stream(f):
#print("file token stream:", f)
return TokenStream(lambda : f.read(1))
def string_token_stream(s):

19
sallyforth/util.py Normal file
View file

@ -0,0 +1,19 @@
def get_attribute(x, name):
return getattr(x, name, None)
class word:
def __init__(self, name=None, immediate=False):
self.name = name
self.immediate = immediate
def __call__(self, f):
f.forth_word = True
if self.name:
f.forth_name = self.name
else:
f.forth_name = f.__name__
f.forth_type = 'primitive'
f.forth_inline = False
f.forth_immediate = self.immediate
return f

52
sallyforth/wrappers.py Normal file
View file

@ -0,0 +1,52 @@
class Reference:
def __init__(self, var):
self.var = var
def __call__(self, forth):
#print("indirect call on", self.var.name)
return self.var.value(forth)
@property
def forth_immediate(self):
return self.var.value.forth_immediate
@property
def forth_contents(self):
return self.var.value.forth_contents
@property
def forth_primitive(self):
return self.var.value.forth_primitive
@property
def forth_name(self):
return self.var.value.forth_name
@property
def forth_inline(self):
return self.var.value.forth_inline
def ref_f(var):
return Reference(var)
def value_f(value):
def push_constant(f):
f.stack.push(value)
push_constant.forth_inline = False
push_constant.forth_primitive = True
push_constant.forth_name = 'pushv'
push_constant.forth_immediate = False
return push_constant
def inner_f(contents):
def inner(forth):
for fn in contents:
fn(forth)
inner.forth_primitive = False
inner.forth_immediate = False
inner.forth_contents = contents
inner.forth_inline = False
return inner
def noop(value):
pass