mirror of
https://github.com/russolsen/sallyforth
synced 2024-12-25 21:58:18 +01:00
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:
parent
ed5ccfd261
commit
9e9573b3dd
21 changed files with 728 additions and 1126 deletions
88
sallyforth/0.sf
Normal file
88
sallyforth/0.sf
Normal 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
|
|
@ -1,2 +0,0 @@
|
|||
class Arglist(list):
|
||||
pass
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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 ;
|
|
@ -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 }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
;
|
||||
}
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 }
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
19
sallyforth/util.py
Normal 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
52
sallyforth/wrappers.py
Normal 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
|
Loading…
Reference in a new issue