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 importlib
|
||||||
import os
|
from pprint import pprint
|
||||||
from compiler import Compiler
|
|
||||||
from arglist import Arglist
|
|
||||||
from operator_words import w_not
|
|
||||||
|
|
||||||
def const_f(value):
|
@word('raise')
|
||||||
def x(f, i):
|
def w_raise(f):
|
||||||
#print("const f, pushing", value)
|
ex = f.stack.pop()
|
||||||
f.stack.push(value)
|
raise ex
|
||||||
return i + 1
|
|
||||||
return x
|
|
||||||
|
|
||||||
def native_function_handler(func):
|
@word(immediate=True)
|
||||||
def handle(forth, i):
|
def readtoken(f):
|
||||||
args = forth.stack.pop()
|
t = f.stream.get_token()
|
||||||
#print(f"Native fun, calling {func}({args})")
|
def push_token(xforth):
|
||||||
result = func(*args)
|
xforth.stack.push(t)
|
||||||
#print(f'Result: {result}')
|
return push_token
|
||||||
forth.stack.push(result)
|
|
||||||
#print("pushed result")
|
|
||||||
return i + 1
|
|
||||||
return handle
|
|
||||||
|
|
||||||
def import_native_module(forth, m, alias=None, excludes=[]):
|
@word("!!")
|
||||||
if not alias:
|
def w_call(f):
|
||||||
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):
|
|
||||||
func = f.stack.pop()
|
func = f.stack.pop()
|
||||||
args = f.stack.pop()
|
args = f.stack.pop()
|
||||||
#print('f', f, 'args', args)
|
#print('f', f, 'args', args)
|
||||||
|
@ -164,177 +27,171 @@ def w_call(f, i):
|
||||||
except:
|
except:
|
||||||
print(f'Error executing {func}({args})')
|
print(f'Error executing {func}({args})')
|
||||||
raise
|
raise
|
||||||
# print('result', result)
|
#print('result', result)
|
||||||
f.stack.push(result)
|
f.stack.push(result)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_kwcall(f, i):
|
@word()
|
||||||
func = f.stack.pop()
|
def unique(f):
|
||||||
kws = f.stack.pop()
|
f.stack.push(Unique())
|
||||||
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
|
|
||||||
|
|
||||||
def w_nl(f, i):
|
@word()
|
||||||
print()
|
def load(f):
|
||||||
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()
|
|
||||||
name = f.stack.pop()
|
name = f.stack.pop()
|
||||||
print(f'name: {name} flag {flag}')
|
m = importlib.import_module(name)
|
||||||
f.namespace[name].immediate = flag
|
f.set_constant(name, m)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_should_not_happen(forth, i):
|
@word('import')
|
||||||
print('Should not execute this word!')
|
def w_import(f):
|
||||||
raise ValueError
|
name = f.stack.pop()
|
||||||
|
m = importlib.import_module(name)
|
||||||
|
f.ns.import_native_module(m)
|
||||||
|
|
||||||
def i_if(forth, i):
|
@word()
|
||||||
#print('w_if')
|
def lexicon(f):
|
||||||
compiler = forth.compiler
|
name = f.stack.pop()
|
||||||
compiler.push_offset()
|
m = importlib.import_module(name)
|
||||||
compiler.push_offset()
|
f.ns.import_from_module(m)
|
||||||
compiler.add_instruction(w_should_not_happen)
|
|
||||||
return i+1
|
|
||||||
|
|
||||||
def i_ifnot(forth, i):
|
@word('source')
|
||||||
compiler = forth.compiler
|
def w_source(f):
|
||||||
compiler.add_instruction(w_not)
|
path = f.stack.pop()
|
||||||
compiler.push_offset()
|
f.eval_file(path)
|
||||||
compiler.push_offset()
|
|
||||||
compiler.add_instruction(w_should_not_happen)
|
|
||||||
return i+2
|
|
||||||
|
|
||||||
def i_then(forth, i):
|
@word('alias')
|
||||||
compiler = forth.compiler
|
def w_alias(f):
|
||||||
else_offset = compiler.pop_offset()
|
new_name = f.stack.pop()
|
||||||
if_offset = compiler.pop_offset()
|
old_name = f.stack.pop()
|
||||||
then_offset = compiler.offset()
|
f.alias(new_name, old_name)
|
||||||
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
|
|
||||||
|
|
||||||
def i_else(forth, i):
|
@word()
|
||||||
compiler = forth.compiler
|
def rawdef(f):
|
||||||
compiler.pop_offset()
|
name = f.stack.pop()
|
||||||
compiler.push_offset()
|
value = f.stack.pop()
|
||||||
compiler.add_instruction(w_should_not_happen)
|
f.set(name, value)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def i_do(forth, i):
|
@word("=!")
|
||||||
#print('w_do')
|
def equal_bang(f):
|
||||||
compiler = forth.compiler
|
name = f.stack.pop()
|
||||||
compiler.push_offset()
|
value = f.stack.pop()
|
||||||
compiler.add_instruction(w_should_not_happen)
|
f.set_constant(name, value)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def i_begin(forth, i):
|
@word("*prompt*")
|
||||||
compiler = forth.compiler
|
def promptword(f):
|
||||||
compiler.push_offset()
|
f.stack.push(">> ")
|
||||||
return i
|
|
||||||
|
|
||||||
def i_while(forth, i):
|
|
||||||
compiler = forth.compiler
|
|
||||||
compiler.push_offset()
|
|
||||||
compiler.add_instruction(w_should_not_happen)
|
|
||||||
return i+1
|
|
||||||
|
|
||||||
def i_repeat(forth, i):
|
@word()
|
||||||
compiler = forth.compiler
|
def lookup(f):
|
||||||
while_offset = compiler.pop_offset()
|
name = f.stack.pop()
|
||||||
begin_offset = compiler.pop_offset()
|
f.stack.push(f.ns[name])
|
||||||
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
|
|
||||||
|
|
||||||
def w_marker1(f, i):
|
@word()
|
||||||
print("marker1")
|
def forget(f):
|
||||||
return i+1
|
name = f.stack.pop()
|
||||||
|
del f.ns[name]
|
||||||
|
|
||||||
def w_marker2(f, i):
|
@word()
|
||||||
print("marker3")
|
def p(f):
|
||||||
return i+1
|
print(f.stack.pop())
|
||||||
|
|
||||||
def w_marker3(f, i):
|
@word()
|
||||||
print("marker3")
|
def nl(f):
|
||||||
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=' ')
|
|
||||||
print()
|
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):
|
@word('.')
|
||||||
ex = f.stack.pop()
|
def dot(f):
|
||||||
raise ex
|
print(f.stack.pop(), end='')
|
||||||
return i+1
|
|
||||||
|
@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:
|
LBrace = Token('word', '{')
|
||||||
def __init__(self, name=None):
|
RBrace = Token('word', '}')
|
||||||
self.name = name
|
|
||||||
self.instructions = []
|
|
||||||
self.offsets = Stack()
|
|
||||||
self.inline = False
|
|
||||||
|
|
||||||
def add_instruction(self, ins):
|
def compile_word(forth, w):
|
||||||
self.instructions.append(ins)
|
name = w.value
|
||||||
|
var = forth.ns[name]
|
||||||
|
value = var.value
|
||||||
|
|
||||||
def add_instructions(self, instructions):
|
if value.forth_immediate:
|
||||||
self.instructions.extend(instructions)
|
return value(forth)
|
||||||
|
elif var.dynamic:
|
||||||
|
return ref_f(var)
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
def offset(self):
|
def compile_token(forth, t):
|
||||||
return len(self.instructions)
|
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):
|
def compile_value(contents, v):
|
||||||
if not value:
|
#print("compiling", v, v.__dict__)
|
||||||
self.offsets.push(self.offset())
|
if v.forth_inline and v.forth_contents:
|
||||||
else:
|
contents.extend(v.forth_contents)
|
||||||
self.offsets.push(value)
|
else:
|
||||||
#print("compiler stack", self.offsets.stack)
|
contents.append(v)
|
||||||
|
return contents
|
||||||
|
|
||||||
def pop_offset(self):
|
def compile_next(forth, stream, current_token=None):
|
||||||
return self.offsets.pop()
|
if current_token:
|
||||||
|
t = current_token
|
||||||
|
else:
|
||||||
|
t = stream.get_token()
|
||||||
|
|
||||||
def _str__(self):
|
if t == None:
|
||||||
result = f'Compiler {name}'
|
return None
|
||||||
for i in self.instructions:
|
|
||||||
result += str(i)
|
|
||||||
result += ' '
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
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 unique import Unique
|
||||||
from arglist import Arglist
|
|
||||||
|
|
||||||
def w_unique(f, ip): # pushes a uique object.
|
@word('[list]')
|
||||||
f.stack.push(Unique())
|
def w_bounded_list(f):
|
||||||
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):
|
|
||||||
"""Create a list from delimted values on the stack.
|
"""Create a list from delimted values on the stack.
|
||||||
[list]
|
[list]
|
||||||
(marker a b c marker -- [a b c]
|
(marker a b c marker -- [a b c]
|
||||||
|
@ -59,22 +19,54 @@ def w_bounded_list(f, ip):
|
||||||
x = f.stack.pop()
|
x = f.stack.pop()
|
||||||
l.reverse()
|
l.reverse()
|
||||||
f.stack.push(l)
|
f.stack.push(l)
|
||||||
return ip+1
|
|
||||||
|
|
||||||
def w_to_arglist(f, ip):
|
@word('->list')
|
||||||
l = f.stack.pop()
|
def w_list(f):
|
||||||
f.stack.push(Arglist(l))
|
|
||||||
return ip+1
|
|
||||||
|
|
||||||
def w_list(f, ip): # ->list
|
|
||||||
n = f.stack.pop()
|
n = f.stack.pop()
|
||||||
l = []
|
l = []
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
l.append(f.stack.pop())
|
l.append(f.stack.pop())
|
||||||
f.stack.push(l)
|
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()
|
contents = f.stack.pop()
|
||||||
print("Contents:", contents)
|
print("Contents:", contents)
|
||||||
result = contents[0]
|
result = contents[0]
|
||||||
|
@ -82,79 +74,28 @@ def w_thread(f, i): # @@
|
||||||
print("Result:", result)
|
print("Result:", result)
|
||||||
if isinstance(field, str) and hasattr(result, field):
|
if isinstance(field, str) and hasattr(result, field):
|
||||||
result = getattr(result, field) # result.field
|
result = getattr(result, field) # result.field
|
||||||
elif isinstance(field, Arglist):
|
|
||||||
result = result(*field) # result(*field)
|
|
||||||
else:
|
else:
|
||||||
result = result[field] # result[field]
|
result = result[field] # result[field]
|
||||||
f.stack.push(result)
|
f.stack.push(result)
|
||||||
return i+1
|
|
||||||
|
|
||||||
|
@word('list->map')
|
||||||
ListMarker = object()
|
def w_list_to_map(f): # list->map
|
||||||
|
|
||||||
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
|
|
||||||
l = f.stack.pop()
|
l = f.stack.pop()
|
||||||
result = {}
|
result = {}
|
||||||
for i in range(0, len(l), 2):
|
for i in range(0, len(l), 2):
|
||||||
result[l[i]] = l[i+1]
|
result[l[i]] = l[i+1]
|
||||||
f.stack.push(result)
|
f.stack.push(result)
|
||||||
return ip+1
|
|
||||||
|
|
||||||
def w_get(f, i):
|
@word()
|
||||||
|
def w_get(f):
|
||||||
name = f.stack.pop()
|
name = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
result = m[name]
|
result = m[name]
|
||||||
f.stack.push(result)
|
f.stack.push(result)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_getattribute(f, i):
|
@word()
|
||||||
|
def w_getattribute(f):
|
||||||
name = f.stack.pop()
|
name = f.stack.pop()
|
||||||
x = f.stack.pop()
|
x = f.stack.pop()
|
||||||
result = x.__getattribute__(name)
|
result = x.__getattribute__(name)
|
||||||
f.stack.push(result)
|
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 ;
|
: open { builtins/open !!1 }
|
||||||
: close <. $? 'close .> !!0 drop ;
|
: close { <. $? 'close .> !!0 drop }
|
||||||
|
|
||||||
: read-file (path -- contents) open dup <. $? 'read .> !!0 swap close ;
|
: read-file (path -- contents) { open dup <. $? 'read .> !!0 swap close }
|
||||||
: read-lines (path -- contents) open dup <. $? 'readlines .> !!0 swap close ;
|
: read-lines (path -- contents) { open dup <. $? 'readlines .> !!0 swap close }
|
||||||
|
|
||||||
: read-line (prompt -- input-line)
|
: read-line (prompt -- input-line) { builtins/input !!1 }
|
||||||
builtins/input !!1
|
|
||||||
;
|
|
||||||
|
|
||||||
\ : read-next nexttoken second ;
|
|
||||||
|
|
|
@ -1,212 +1,82 @@
|
||||||
import sys
|
import sys
|
||||||
from os import path
|
import os
|
||||||
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
|
|
||||||
from stack import Stack
|
from stack import Stack
|
||||||
from namespace import Namespace
|
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:
|
class Forth:
|
||||||
def __init__(self, startup=None):
|
def __init__(self):
|
||||||
self.streams = Stack()
|
|
||||||
self.stack = Stack()
|
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')
|
def set_constant(self, name, value):
|
||||||
self.namespace = self.forth_ns
|
return self.ns.set(name, value_f(value))
|
||||||
user_ns = self.make_namespace('user', {}, [self.forth_ns])
|
|
||||||
|
|
||||||
self.defword('*prompt*', const_f('SallyForth>> '))
|
def set(self, name, fvalue):
|
||||||
self.defword('*source*', const_f(__file__))
|
return self.ns.set(name, fvalue)
|
||||||
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))
|
|
||||||
|
|
||||||
self.forth_ns.import_from_module(basic_words)
|
def get(self, name, def_value=None):
|
||||||
self.forth_ns.import_from_module(data_words)
|
if name in self.ns:
|
||||||
self.forth_ns.import_from_module(operator_words)
|
return self.ns[name]
|
||||||
self.forth_ns.import_from_module(stack_words)
|
return def_value
|
||||||
self.forth_ns.import_from_module(os_words)
|
|
||||||
self.namespace.alias("*execute-command*", "execute")
|
|
||||||
|
|
||||||
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:
|
def eval_stream(self, stream):
|
||||||
self.execute_file(startup)
|
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):
|
def eval_string(self, s):
|
||||||
return self.namespace.set(name, value)
|
self.eval_stream(ts.string_token_stream(s))
|
||||||
|
|
||||||
def defvar(self, name, value):
|
def eval_string_r(self, s):
|
||||||
return self.defword(name, const_f(value))
|
self.eval_string(s)
|
||||||
|
|
||||||
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)
|
|
||||||
return self.stack.pop()
|
return self.stack.pop()
|
||||||
|
|
||||||
def read_next_token(self):
|
def lookup(self, name):
|
||||||
s = self.streams.peek()
|
return self.ns[name]
|
||||||
return s.get_token()
|
|
||||||
|
|
||||||
def py_evaluate(self, s, *args):
|
if __name__ == "__main__":
|
||||||
#print(f'Evaluate: token [{token}] args <<{args}>>')
|
x = 0
|
||||||
rargs = list(args)
|
|
||||||
rargs.reverse()
|
def pmt():
|
||||||
if rargs:
|
global x
|
||||||
for a in rargs:
|
x += 1
|
||||||
#print("pushing", a);
|
return f'Yes{x}>> '
|
||||||
self.stack.push(a)
|
|
||||||
#print(f'Before eval stack is {str(self.stack)}')
|
pis = ts.PromptInputStream(pmt)
|
||||||
return self.evaluate_string(s)
|
tstream = ts.TokenStream(pis.getc)
|
||||||
|
|
||||||
|
forth = Forth()
|
||||||
def macro_expand_token(self, token):
|
forth.eval_stream(tstream)
|
||||||
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)
|
|
||||||
|
|
|
@ -1,47 +1,48 @@
|
||||||
\ Index into the x'th item.
|
\ Index into the x'th item.
|
||||||
: [x] (col key -- value) 1 ->list '__getitem__ .!! ;
|
|
||||||
|
|
||||||
: first (list -- first-item) 0 [x] ;
|
: [x] (col key -- value) { 1 ->list '__getitem__ .!! }
|
||||||
: second (list -- second-item) 1 [x] ;
|
|
||||||
: third (list -- third-item) 2 [x] ;
|
|
||||||
: fourth (list -- fourth-item) 3 [x] ;
|
|
||||||
|
|
||||||
: 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
|
swap
|
||||||
2 ->list
|
2 ->list
|
||||||
builtins/slice
|
builtins/slice
|
||||||
!!
|
!!
|
||||||
;
|
}
|
||||||
|
|
||||||
: take (n list -- first-n-items)
|
: take (n list -- first-n-items) {
|
||||||
swap 0 swap slice \ Make the 0..n slice.
|
swap 0 swap slice \ Make the 0..n slice.
|
||||||
[x] \ Do a[0..n].
|
[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.
|
swap nil slice \ Make the n..None slice.
|
||||||
[x]
|
[x]
|
||||||
;
|
}
|
||||||
|
|
||||||
: n-of (n x -- list-of-x-repeated-n-times)
|
: n-of (n x -- list-of-x-repeated-n-times) {
|
||||||
1 ->list *
|
1 ->list *
|
||||||
;
|
}
|
||||||
|
|
||||||
: len builtins/len !!1 ;
|
: len { builtins/len !!1 }
|
||||||
|
|
||||||
: empty? len zero? ;
|
: empty? { len zero? }
|
||||||
|
|
||||||
: rest (list -- all-but-first) 1 swap skip ;
|
: rest (list -- all-but-first) { 1 swap skip }
|
||||||
: rrest (list -- rest-of-rest) rest rest ;
|
: rrest (list -- rest-of-rest) { rest rest }
|
||||||
: rrrest (list -- all-but-first) rest rest rest ;
|
: rrrest (list -- all-but-first) { rest rest rest }
|
||||||
|
|
||||||
: ffirst (list -- first-of-first) first first ;
|
: ffirst (list -- first-of-first) { first first }
|
||||||
: fffirst (list -- fff-irst) 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
|
dup tbm
|
||||||
<. $? 'append .> !!1
|
<. $? 'append .> !!1
|
||||||
drop
|
drop
|
||||||
;
|
}
|
||||||
|
|
|
@ -1,38 +1,26 @@
|
||||||
class Entry:
|
from util import get_attribute
|
||||||
def __init__(self, name, value, immed):
|
from wrappers import value_f
|
||||||
|
|
||||||
|
class Var:
|
||||||
|
def __init__(self, name, value, dynamic=True):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
self.immediate = immed
|
self.dynamic = dynamic
|
||||||
self.inline = False
|
|
||||||
self.definition = None
|
|
||||||
|
|
||||||
def get_ivalue(self):
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
def get_cvalue(self):
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = f'Entry {self.name} {self.immediate} {self.inline}\n'
|
return f'[[[[Var({self.name}/{self.dynamic}::{self.value})]]]'
|
||||||
for x in self.definition:
|
|
||||||
result += f'{x}\n'
|
def __repr__(self):
|
||||||
return result
|
return str(self)
|
||||||
|
|
||||||
class Namespace:
|
class Namespace:
|
||||||
def __init__(self, name, initial_contents={}, refers=[]):
|
def __init__(self, name):
|
||||||
|
self.contents = {}
|
||||||
self.name = name
|
self.name = name
|
||||||
self.contents = initial_contents.copy()
|
|
||||||
self.refers = refers.copy()
|
|
||||||
|
|
||||||
def alias(self, new_name, existing_name):
|
def alias(self, new_name, existing_name):
|
||||||
self.contents[new_name] = self.contents[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):
|
def import_from_module(self, m):
|
||||||
"""
|
"""
|
||||||
Import all of the word defining functions in
|
Import all of the word defining functions in
|
||||||
|
@ -41,82 +29,55 @@ class Namespace:
|
||||||
"""
|
"""
|
||||||
names = dir(m)
|
names = dir(m)
|
||||||
for name in names:
|
for name in names:
|
||||||
if name.startswith("w_"):
|
value = getattr(m, name)
|
||||||
word_name = name[2::]
|
if get_attribute(value, 'forth_word'):
|
||||||
#print(f'Setting {word_name} to false')
|
forth_name = value.forth_name or name
|
||||||
self.set(word_name, getattr(m, name))
|
var = self.set(forth_name, value, False)
|
||||||
elif name.startswith("i_"):
|
var.immediate = value.forth_immediate
|
||||||
word_name = name[2::]
|
#print(var)
|
||||||
#print(f'Setting {word_name} to true')
|
if var.immediate:
|
||||||
self.set(word_name, getattr(m, name), immediate=True)
|
print(name, 'immediate')
|
||||||
|
|
||||||
def set(self, name, value, cvalue=None, immediate=False):
|
def import_native_module(self, m, alias=None):
|
||||||
if name not in self.contents:
|
if not alias:
|
||||||
entry = Entry(name, value, immediate)
|
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:
|
else:
|
||||||
entry = self[name]
|
var = self.contents[key]
|
||||||
entry.value = value
|
var.value = value
|
||||||
entry.cvalue = cvalue
|
var.dynamic = dynamic
|
||||||
entry.immediate = immediate
|
return var
|
||||||
self.contents[name] = entry
|
|
||||||
return entry
|
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return self.contents.keys()
|
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):
|
def __contains__(self, key):
|
||||||
#print(f'Namespace contains {key}')
|
return self.contents.__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)
|
|
||||||
|
|
||||||
def __delattr__(self, key):
|
def __delattr__(self, key):
|
||||||
return self.contents.__delattr__(key)
|
return self.contents.__delattr__(key)
|
||||||
|
|
||||||
def __setitem__(self, key, x):
|
def __setitem__(self, key, x):
|
||||||
self.contents[key] = x
|
return self.set(key, x)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self.contents.__iter__()
|
return self.contents.__iter__()
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
#print("get item", key, self.name)
|
return self.contents.__getitem__(key)
|
||||||
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)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Namespace({self.name})'
|
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):
|
from util import word
|
||||||
a = f.stack.pop()
|
|
||||||
b = f.stack.pop()
|
|
||||||
f.stack.push(b > a)
|
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_lt(f, i):
|
@word('>')
|
||||||
a = f.stack.pop()
|
def gt(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b < a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b > a)
|
||||||
|
|
||||||
def w_eq(f, i):
|
@word('<')
|
||||||
a = f.stack.pop()
|
def lt(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(a==b)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b < a)
|
||||||
|
|
||||||
def w_le(f, i):
|
@word('=')
|
||||||
a = f.stack.pop()
|
def eq(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b<=a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(a==b)
|
||||||
|
|
||||||
def w_ge(f, i):
|
@word('<=')
|
||||||
a = f.stack.pop()
|
def le(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b>=a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b<=a)
|
||||||
|
|
||||||
def w_add(f, i):
|
@word('>=')
|
||||||
a = f.stack.pop()
|
def ge(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b+a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b>=a)
|
||||||
|
|
||||||
def w_mul(f, i):
|
@word('+')
|
||||||
a = f.stack.pop()
|
def add(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b*a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b+a)
|
||||||
|
|
||||||
def w_sub(f, i):
|
@word('*')
|
||||||
a = f.stack.pop()
|
def mul(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b-a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b*a)
|
||||||
|
|
||||||
def w_div(f, i):
|
@word('-')
|
||||||
a = f.stack.pop()
|
def sub(forth):
|
||||||
b = f.stack.pop()
|
a = forth.stack.pop()
|
||||||
f.stack.push(b/a)
|
b = forth.stack.pop()
|
||||||
return i+1
|
forth.stack.push(b-a)
|
||||||
|
|
||||||
def w_and(f, i):
|
@word('/')
|
||||||
f.stack.push(f.stack.pop() and f.stack.pop())
|
def div(forth):
|
||||||
return i+1
|
a = forth.stack.pop()
|
||||||
|
b = forth.stack.pop()
|
||||||
|
forth.stack.push(b/a)
|
||||||
|
|
||||||
def w_or(f, i):
|
@word('and')
|
||||||
f.stack.push(f.stack.pop() or f.stack.pop())
|
def w_and(forth):
|
||||||
return i+1
|
forth.stack.push(forth.stack.pop() and forth.stack.pop())
|
||||||
|
|
||||||
def w_not(f, i):
|
@word('or')
|
||||||
f.stack.push(not f.stack.pop())
|
def w_or(forth):
|
||||||
return i+1
|
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 os
|
||||||
import sys
|
import sys
|
||||||
|
from util import word
|
||||||
|
|
||||||
def w_fork(f, i):
|
@word('fork')
|
||||||
|
def w_fork(f):
|
||||||
parent_word = f.stack.pop()
|
parent_word = f.stack.pop()
|
||||||
child_word = f.stack.pop()
|
child_word = f.stack.pop()
|
||||||
parent_f = f.namespace.get(parent_word, None).get_ivalue()
|
parent_f = f.namespace.get(parent_word, None).get_ivalue()
|
||||||
|
@ -14,28 +16,26 @@ def w_fork(f, i):
|
||||||
else:
|
else:
|
||||||
print("parent:", pid)
|
print("parent:", pid)
|
||||||
parent_f(f, 0)
|
parent_f(f, 0)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_execvp(f, i):
|
@word('execvp')
|
||||||
|
def w_execvp(f):
|
||||||
args = f.stack.pop()
|
args = f.stack.pop()
|
||||||
path = args[0]
|
path = args[0]
|
||||||
print(f"path {path} args: {args}")
|
print(f"path {path} args: {args}")
|
||||||
os.execvp(path, args)
|
os.execvp(path, args)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_waitpid(f, i):
|
@word('waitpid')
|
||||||
|
def w_waitpid(f):
|
||||||
pid = f.stack.pop()
|
pid = f.stack.pop()
|
||||||
result = os.waitpid(pid, 0)
|
result = os.waitpid(pid, 0)
|
||||||
f.stack.push(result)
|
f.stack.push(result)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_exit(f, i):
|
@word('exit')
|
||||||
|
def w_exit(f):
|
||||||
n = f.stack.pop()
|
n = f.stack.pop()
|
||||||
sys.exit(n)
|
sys.exit(n)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_exit_bang(f, i):
|
@word('exit!')
|
||||||
|
def w_exit_bang(f):
|
||||||
n = f.stack.pop()
|
n = f.stack.pop()
|
||||||
os._exit(n)
|
os._exit(n)
|
||||||
return i+1
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ class Completer:
|
||||||
def __init__(self, f):
|
def __init__(self, f):
|
||||||
self.f = f
|
self.f = f
|
||||||
def complete(self, prefix, index):
|
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:
|
try:
|
||||||
return self.matching_words[index]
|
return self.matching_words[index]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -27,6 +28,7 @@ def setup_readline(history_path, f):
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
readline.parse_and_bind("tab: complete")
|
readline.parse_and_bind("tab: complete")
|
||||||
|
readline.set_completer_delims(' \t\n()[{]}\\|;:\'",')
|
||||||
readline.set_completer(completer.complete)
|
readline.set_completer(completer.complete)
|
||||||
def save_history():
|
def save_history():
|
||||||
readline.write_history_file(history_path)
|
readline.write_history_file(history_path)
|
||||||
|
@ -34,27 +36,24 @@ def setup_readline(history_path, f):
|
||||||
|
|
||||||
def setup_forth():
|
def setup_forth():
|
||||||
source_dir = os.path.dirname(os.path.abspath(__file__))
|
source_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
startup_file = f'{source_dir}/startup.sf'
|
startup_file = f'{source_dir}/0.sf'
|
||||||
|
|
||||||
|
f = Forth()
|
||||||
if os.path.exists(startup_file):
|
if os.path.exists(startup_file):
|
||||||
f = Forth(startup_file)
|
f.eval_file(startup_file)
|
||||||
else:
|
|
||||||
f = Forth()
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def repl(f):
|
def repl(f):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
prompt = f.evaluate_string('*prompt*')
|
prompt = f.eval_string_r('*prompt*')
|
||||||
try:
|
try:
|
||||||
line = input(prompt)
|
line = input(prompt)
|
||||||
line += "\n"
|
line += "\n"
|
||||||
except EOFError:
|
except EOFError:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
f.stack.push(line)
|
f.eval_string(line)
|
||||||
f.execute_string("*execute-command*")
|
|
||||||
#f.execute_string(line)
|
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
|
@ -1,43 +1,33 @@
|
||||||
class Stack:
|
class Stack:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.top = -1
|
self.stack = []
|
||||||
self.stack = 100 * [None]
|
|
||||||
|
|
||||||
def push(self, x):
|
def push(self, x):
|
||||||
#print("stack push", x)
|
self.stack.append(x)
|
||||||
self.top += 1
|
|
||||||
self.stack[self.top] = x
|
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
result = self.stack[self.top]
|
return self.stack.pop()
|
||||||
#print("stack pop", result)
|
|
||||||
self.top -= 1
|
|
||||||
if self.top < -1:
|
|
||||||
print("stack overpop")
|
|
||||||
self.top = -1;
|
|
||||||
raise ValueError("Stack underflow")
|
|
||||||
return result
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for i in range(0, self.top+1):
|
for x in self.stack:
|
||||||
yield self.stack[i]
|
yield x
|
||||||
|
|
||||||
def depth(self):
|
def depth(self):
|
||||||
return self.top + 1
|
return len(self.stack)
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
return self.top == -1
|
return len(self.stack) == 0
|
||||||
|
|
||||||
def peek(self):
|
def peek(self):
|
||||||
return self.stack[self.top]
|
return self.stack[-1]
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.top = -1
|
self.stack = []
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = ''
|
result = ''
|
||||||
for i in range(self.top + 1):
|
for x in self.stack:
|
||||||
result += str(self.stack[i])
|
result += str(x)
|
||||||
result += ' '
|
result += ' '
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -1,101 +1,83 @@
|
||||||
def w_px(f, i):
|
from util import word
|
||||||
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
|
|
||||||
|
|
||||||
def w_reset(f, i):
|
@word()
|
||||||
a = f.stack.reset()
|
def stackdepth(forth):
|
||||||
return i+1
|
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()
|
a = f.stack.pop()
|
||||||
print(a, end='')
|
b = f.stack.pop()
|
||||||
return i+1
|
f.stack.push(a)
|
||||||
|
f.stack.push(b)
|
||||||
|
|
||||||
|
@word()
|
||||||
|
def tmb(f): # A noop
|
||||||
|
pass
|
||||||
|
|
||||||
def w_stackdepth(f, i):
|
@word()
|
||||||
d = f.stack.depth()
|
def tbm(f):
|
||||||
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):
|
|
||||||
t = f.stack.pop()
|
t = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
f.stack.push(m)
|
f.stack.push(m)
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
f.stack.push(t)
|
f.stack.push(t)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_bmt(f, i):
|
@word()
|
||||||
|
def bmt(f):
|
||||||
t = f.stack.pop()
|
t = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
f.stack.push(t)
|
f.stack.push(t)
|
||||||
f.stack.push(m)
|
f.stack.push(m)
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_btm(f, i):
|
@word()
|
||||||
|
def btm(f):
|
||||||
t = f.stack.pop()
|
t = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
f.stack.push(m)
|
f.stack.push(m)
|
||||||
f.stack.push(t)
|
f.stack.push(t)
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_mtb(f, i):
|
@word()
|
||||||
|
def mtb(f):
|
||||||
t = f.stack.pop()
|
t = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
f.stack.push(t)
|
f.stack.push(t)
|
||||||
f.stack.push(m)
|
f.stack.push(m)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_mbt(f, i):
|
@word()
|
||||||
|
def mbt(f):
|
||||||
t = f.stack.pop()
|
t = f.stack.pop()
|
||||||
m = f.stack.pop()
|
m = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
f.stack.push(t)
|
f.stack.push(t)
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
f.stack.push(m)
|
f.stack.push(m)
|
||||||
return i+1
|
|
||||||
|
|
||||||
def w_rot(f, i):
|
@word()
|
||||||
|
def rot(f):
|
||||||
c = f.stack.pop()
|
c = f.stack.pop()
|
||||||
b = f.stack.pop()
|
b = f.stack.pop()
|
||||||
a = f.stack.pop()
|
a = f.stack.pop()
|
||||||
f.stack.push(b)
|
f.stack.push(b)
|
||||||
f.stack.push(c)
|
f.stack.push(c)
|
||||||
f.stack.push(a)
|
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
|
2 3 * 6 = "2*3 = 6" assert
|
||||||
100 5 / 20 = "100 divided by 5 is 20." 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
|
0 =0 "Zero is equal to itself." assert
|
||||||
1 =0 not "One is not =0." 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
|
\ 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
|
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
|
push64 64 = "A word can use primitive and sec words." assert
|
||||||
|
|
||||||
\ Logic
|
\ 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 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
|
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 true 1-or-2 1 = "True part of ifelse fires." assert
|
||||||
reset false 1-or-2 2 = "False 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
|
\ Name lookup and calls
|
||||||
|
|
||||||
"12" <. builtins 'len .> !!1 2 = "Can use bracket dot notation." assert
|
"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
|
\ Lists
|
||||||
|
|
||||||
|
@ -94,10 +94,10 @@ reset false 1-or-2 2 = "False part of ifelse fires." assert
|
||||||
|
|
||||||
\ Loop
|
\ 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
|
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
|
888 zero-trip-while 888 = "While should handle zero trip case." assert
|
||||||
|
|
|
@ -14,6 +14,17 @@ class Token:
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.value = value
|
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):
|
def isstring(self):
|
||||||
return self.kind == 'string'
|
return self.kind == 'string'
|
||||||
|
|
||||||
|
@ -30,7 +41,7 @@ class Token:
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Token[{self.kind} => {self.value}]'
|
return f'Token {self.kind} => {self.value}'
|
||||||
|
|
||||||
|
|
||||||
class PromptInputStream:
|
class PromptInputStream:
|
||||||
|
@ -52,6 +63,7 @@ class PromptInputStream:
|
||||||
|
|
||||||
class TokenStream:
|
class TokenStream:
|
||||||
def __init__(self, read_f):
|
def __init__(self, read_f):
|
||||||
|
#print("Tokenstream", read_f)
|
||||||
self.read_f = read_f
|
self.read_f = read_f
|
||||||
|
|
||||||
def whitespace(self, ch):
|
def whitespace(self, ch):
|
||||||
|
@ -73,6 +85,9 @@ class TokenStream:
|
||||||
return Token('string', token)
|
return Token('string', token)
|
||||||
if state in ['word']:
|
if state in ['word']:
|
||||||
return Token('word', token)
|
return Token('word', token)
|
||||||
|
if state == 'number':
|
||||||
|
return Token('number', token)
|
||||||
|
#print("x get returning NONE")
|
||||||
return None
|
return None
|
||||||
elif state == 'start' and ch == ':':
|
elif state == 'start' and ch == ':':
|
||||||
token = ch
|
token = ch
|
||||||
|
@ -101,7 +116,8 @@ class TokenStream:
|
||||||
token += ch
|
token += ch
|
||||||
elif state == 'number' and self.whitespace(ch):
|
elif state == 'number' and self.whitespace(ch):
|
||||||
n = to_number(token)
|
n = to_number(token)
|
||||||
if n:
|
if n != None:
|
||||||
|
#print("returning number", n)
|
||||||
return Token('number', n)
|
return Token('number', n)
|
||||||
else:
|
else:
|
||||||
return Token('word', token)
|
return Token('word', token)
|
||||||
|
@ -111,13 +127,14 @@ class TokenStream:
|
||||||
return Token('string', token)
|
return Token('string', token)
|
||||||
elif state == 'keyword' and self.whitespace(ch):
|
elif state == 'keyword' and self.whitespace(ch):
|
||||||
state = 'start'
|
state = 'start'
|
||||||
if len(token) == 1:
|
if token in [':']:
|
||||||
return Token('word', token)
|
return Token('word', token)
|
||||||
return Token('keyword', token)
|
return Token('keyword', token)
|
||||||
elif state in ['word', 'dqstring', 'sqstring', 'number', 'keyword']:
|
elif state in ['word', 'dqstring', 'sqstring', 'number', 'keyword']:
|
||||||
token += ch
|
token += ch
|
||||||
|
|
||||||
def file_token_stream(f):
|
def file_token_stream(f):
|
||||||
|
#print("file token stream:", f)
|
||||||
return TokenStream(lambda : f.read(1))
|
return TokenStream(lambda : f.read(1))
|
||||||
|
|
||||||
def string_token_stream(s):
|
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