mirror of
https://github.com/russolsen/sallyforth
synced 2024-12-25 21:58:18 +01:00
Clean up forth eval/execute, make repl more forth driven.
This commit is contained in:
parent
62acb4ab95
commit
a956438aec
11 changed files with 214 additions and 59 deletions
|
@ -32,14 +32,14 @@ def import_native_module(forth, m, alias=None, excludes=[]):
|
|||
val = getattr(m, name)
|
||||
forth.namespace.set(localname, const_f(val))
|
||||
|
||||
def xx_w_nexttoken(f, i):
|
||||
token = f.read_next_token()
|
||||
f.stack.push(token)
|
||||
return i+1
|
||||
|
||||
def w_eval(f, i):
|
||||
token = f.stack.pop()
|
||||
f.execute_token(token)
|
||||
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):
|
||||
|
@ -155,6 +155,20 @@ def w_call(f, i):
|
|||
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
|
||||
|
||||
def w_nl(f, i):
|
||||
print()
|
||||
return i+1
|
||||
|
@ -165,11 +179,16 @@ def w_return(f, i):
|
|||
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)
|
||||
forth.defword(name, word_f)
|
||||
entry = forth.defword(name, word_f)
|
||||
entry.inline = forth.compiler.inline
|
||||
entry.definition = forth.compiler.instructions
|
||||
forth.compiler = None
|
||||
return i+1
|
||||
|
||||
|
@ -277,4 +296,7 @@ def w_enlist(f, i):
|
|||
f.stack.push([x])
|
||||
return i+1
|
||||
|
||||
|
||||
def w_raise(f, i):
|
||||
ex = f.stack.pop()
|
||||
raise ex
|
||||
return i+1
|
||||
|
|
|
@ -5,10 +5,14 @@ class Compiler:
|
|||
self.name = name
|
||||
self.instructions = []
|
||||
self.offsets = Stack()
|
||||
self.inline = False
|
||||
|
||||
def add_instruction(self, ins):
|
||||
self.instructions.append(ins)
|
||||
|
||||
def add_instructions(self, instructions):
|
||||
self.instructions.extend(instructions)
|
||||
|
||||
def offset(self):
|
||||
return len(self.instructions)
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
|
||||
: prompt-and-run ( -- prog-status)
|
||||
">> " read-line
|
||||
dup "x" =
|
||||
dup "q" =
|
||||
if
|
||||
"Exit!" p
|
||||
"Quit!" p
|
||||
drop
|
||||
else
|
||||
tokenize
|
||||
|
@ -47,3 +47,5 @@
|
|||
recur
|
||||
then
|
||||
;
|
||||
|
||||
: % "Toggle" p ;
|
||||
|
|
|
@ -50,10 +50,10 @@ class Forth:
|
|||
self.namespace = user_ns
|
||||
|
||||
def defword(self, name, value):
|
||||
self.namespace.set(name, value)
|
||||
return self.namespace.set(name, value)
|
||||
|
||||
def defvar(self, name, value):
|
||||
self.defword(name, const_f(value))
|
||||
return self.defword(name, const_f(value))
|
||||
|
||||
def compiling(self):
|
||||
return self.compiler
|
||||
|
@ -61,7 +61,7 @@ class Forth:
|
|||
def _compile_token(self, kind, token):
|
||||
#print(f"compile: {self.compiler.name}: {token}")
|
||||
if self.compiler.name == None:
|
||||
print(f'Compiling {token}')
|
||||
#print(f'Compiling {token}')
|
||||
self.compiler.name = token
|
||||
return
|
||||
|
||||
|
@ -75,6 +75,8 @@ class Forth:
|
|||
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)
|
||||
|
@ -82,12 +84,13 @@ class Forth:
|
|||
|
||||
n = to_number(token)
|
||||
if n == None:
|
||||
print(f'{token}? Compile of {self.compiler.name} terminated.')
|
||||
print(f'[{token}]?? Compile of [{self.compiler.name}] terminated.')
|
||||
self.compiler = None
|
||||
else:
|
||||
self.compiler.add_instruction(const_f(n))
|
||||
|
||||
def _eval_token(self, kind, token):
|
||||
#print(f'eval token {token} kind {kind}')
|
||||
if kind in ['dqstring', 'sqstring']:
|
||||
self.stack.push(token)
|
||||
return
|
||||
|
@ -153,7 +156,7 @@ class Forth:
|
|||
for a in rargs:
|
||||
# print("pushing", a);
|
||||
self.stack.push(a)
|
||||
print(f'Before eval stack is {str(self.stack)}')
|
||||
#print(f'Before eval stack is {str(self.stack)}')
|
||||
return self.evaluate_string(s)
|
||||
|
||||
|
||||
|
@ -168,7 +171,7 @@ class Forth:
|
|||
for part in parts[1::]:
|
||||
result.append(['sqstring', part])
|
||||
result.append(['word', '.>'])
|
||||
print(result)
|
||||
#print(result)
|
||||
return result
|
||||
|
||||
def set_ns(self, ns_name):
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
class Entry:
|
||||
def __init__(self, name, value, immediate):
|
||||
def __init__(self, name, value, immed):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.immediate = immediate
|
||||
self.immediate = immed
|
||||
self.inline = False
|
||||
self.definition = None
|
||||
|
||||
def get_ivalue(self):
|
||||
return self.value
|
||||
|
@ -11,7 +13,10 @@ class Entry:
|
|||
return self.value
|
||||
|
||||
def __str__(self):
|
||||
return f'Entry {self.name} {self.immediate}'
|
||||
result = f'Entry {self.name} {self.immediate} {self.inline}\n'
|
||||
for x in self.definition:
|
||||
result += f'{x}\n'
|
||||
return result
|
||||
|
||||
class Namespace:
|
||||
def __init__(self, name, initial_contents={}, refers=[]):
|
||||
|
@ -51,6 +56,7 @@ class Namespace:
|
|||
entry.cvalue = cvalue
|
||||
entry.immediate = immediate
|
||||
self.contents[name] = entry
|
||||
return entry
|
||||
|
||||
def keys(self):
|
||||
return self.contents.keys()
|
||||
|
|
|
@ -44,16 +44,21 @@ def setup_forth():
|
|||
|
||||
def repl(f):
|
||||
while True:
|
||||
prompt = f.evaluate_string('*prompt*')
|
||||
try:
|
||||
line = input(prompt)
|
||||
except EOFError:
|
||||
return
|
||||
try:
|
||||
f.execute_string(line)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
prompt = f.evaluate_string('*prompt*')
|
||||
try:
|
||||
line = input(prompt)
|
||||
line += "\n"
|
||||
except EOFError:
|
||||
return
|
||||
try:
|
||||
f.stack.push(line)
|
||||
f.execute_string("*execute-command*")
|
||||
#f.execute_string(line)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
f = setup_forth()
|
||||
|
|
|
@ -23,6 +23,9 @@ class Stack:
|
|||
for i in range(0, self.top+1):
|
||||
yield self.stack[i]
|
||||
|
||||
def depth(self):
|
||||
return self.top + 1
|
||||
|
||||
def empty(self):
|
||||
return self.top == -1
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ def w_dot(f, i):
|
|||
print(a, end='')
|
||||
return i+1
|
||||
|
||||
def w_stackdepth(f, i):
|
||||
d = f.stack.depth()
|
||||
f.stack.push(d)
|
||||
|
||||
def w_splat(f, i):
|
||||
l = f.stack.pop()
|
||||
l.reverse()
|
||||
|
@ -29,12 +33,6 @@ def w_dup(f, i):
|
|||
return i+1
|
||||
|
||||
def w_tmb(f, i): # A noop
|
||||
# t = f.stack.pop()
|
||||
# m = f.stack.pop()
|
||||
# b = f.stack.pop()
|
||||
# f.stack.push(b)
|
||||
# f.stack.push(m)
|
||||
# f.stack.push(t)
|
||||
return i+1
|
||||
|
||||
def w_tbm(f, i):
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"io" require
|
||||
"time" require
|
||||
|
||||
'builtins import
|
||||
|
||||
\ Basic aliases
|
||||
|
||||
'None 'nil alias
|
||||
|
@ -36,13 +38,14 @@
|
|||
'current_ns '*ns* alias
|
||||
|
||||
: *prompt* "Sally> " ;
|
||||
: *execute-command* stack execute ;
|
||||
|
||||
\ Make a list.
|
||||
'list-marker unique def
|
||||
: [ list-marker ;
|
||||
: ] list-marker [list] ;
|
||||
: [ list-marker inline ;
|
||||
: ] list-marker [list] inline ;
|
||||
|
||||
: [] ( -- <empty list>) [ ] ;
|
||||
: [] ( -- <empty list>) [ ] inline ;
|
||||
|
||||
|
||||
\ Look up attributes on a value.
|
||||
|
@ -51,16 +54,16 @@
|
|||
: $? swap ;
|
||||
|
||||
\ Call native functions with various # arguments.
|
||||
: !!0 [] swap !! ;
|
||||
: !!1 swap 1 ->list swap !! ;
|
||||
: !!2 swap 2 ->list swap !! ;
|
||||
: !!0 [] swap !! inline ;
|
||||
: !!1 swap 1 ->list swap !! inline ;
|
||||
: !!2 swap 2 ->list swap !! inline ;
|
||||
|
||||
\ Make a map.
|
||||
'map-marker unique def
|
||||
: { map-marker ;
|
||||
: } map-marker [list] list->map ;
|
||||
: { map-marker inline ;
|
||||
: } map-marker [list] list->map inline ;
|
||||
|
||||
: {} ( -- <empty map>) { } ;
|
||||
: {} ( -- <empty map>) { } inline ;
|
||||
|
||||
|
||||
\ Make a set.
|
||||
|
@ -71,16 +74,21 @@
|
|||
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>) {{ }} ;
|
||||
: {{}} ( -- <empty set>) {{ }} inline ;
|
||||
|
||||
: [: [ ;
|
||||
: :] ] ->arglist ;
|
||||
: [: [ 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 ;
|
||||
|
@ -89,22 +97,22 @@
|
|||
|
||||
: hello "Hello" . nl ;
|
||||
|
||||
: >0 0 > ;
|
||||
: =0 0 = ;
|
||||
: <1 1 < ;
|
||||
: <0 0 < ;
|
||||
: >1 1 > ;
|
||||
: <1 1 < ;
|
||||
: >0 0 > inline ;
|
||||
: =0 0 = inline ;
|
||||
: <1 1 < inline ;
|
||||
: <0 0 < inline ;
|
||||
: >1 1 > inline ;
|
||||
: <1 1 < inline ;
|
||||
|
||||
: p . nl ;
|
||||
: top dup p ;
|
||||
|
||||
: -- -1 + ;
|
||||
: ++ 1 + ;
|
||||
: *2 2 * ;
|
||||
: pos? 0 > ;
|
||||
: neg? 0 < ;
|
||||
: zero? 0 = ;
|
||||
: -- -1 + inline ;
|
||||
: ++ 1 + inline ;
|
||||
: *2 2 * inline ;
|
||||
: pos? 0 > inline ;
|
||||
: neg? 0 < inline ;
|
||||
: zero? 0 = inline ;
|
||||
|
||||
: exists? os.path.exists !!1 ;
|
||||
|
||||
|
@ -123,7 +131,16 @@
|
|||
: .!!2 (obj a1 a2 method-name -- result ) swap 2 ->list swap .!! ;
|
||||
: .!!3 (obj a1 a2 a3 method-name -- result ) swap 3 ->list swap .!! ;
|
||||
|
||||
\ todo : tokenize #forth.tokenizer.tokenize !!1 ;
|
||||
: assert ( bool msg -- )
|
||||
swap
|
||||
if
|
||||
"OK " . p
|
||||
else
|
||||
#builtins.AssertionError !!1
|
||||
raise
|
||||
then
|
||||
;
|
||||
|
||||
|
||||
"string.sf" source
|
||||
"list.sf" source
|
||||
|
|
93
sallyforth/test.sf
Normal file
93
sallyforth/test.sf
Normal file
|
@ -0,0 +1,93 @@
|
|||
|
||||
\ Test stack and arithmetic.
|
||||
|
||||
reset stackdepth 0 = "Stack starts empty." assert
|
||||
reset 111 stackdepth 1 = "One item on stack" assert
|
||||
reset 111 222 333 stackdepth 3 = "Three items on stack" assert
|
||||
|
||||
reset 1 2 3 reset stackdepth 0 = "Reset empties the stack." assert
|
||||
|
||||
10 10 = "Stack and equality." assert
|
||||
1 2 < "1 less than 2." assert
|
||||
2 1 > "2 bigger than 1." assert
|
||||
99 99 = "99 is equal to itself." assert
|
||||
|
||||
1 1 + 2 = "1 + 1 = 2" assert
|
||||
10 99 + 109 = "10 + 99 = 109" assert
|
||||
10 1 - 9 = "10 - 1 = 9" 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
|
||||
|
||||
1 pos? "One is positive." assert
|
||||
-1 pos? not "One is not positive." assert
|
||||
|
||||
1 ++ 2 = "1++ is two." assert
|
||||
0 -- -1 = "0-- is -1." assert
|
||||
|
||||
\ Booleans
|
||||
|
||||
true "True is true." assert
|
||||
true not false = "Not true is false" assert
|
||||
false not "Not false is true." assert
|
||||
|
||||
true true or "T or T is T." assert
|
||||
true false or "T or F is T." assert
|
||||
false true or "F or T is T." assert
|
||||
false false or not "F or F is F." assert
|
||||
|
||||
true true and "T and T is T." assert
|
||||
true false and not "T and F is F." assert
|
||||
false true and not "F and T is F." assert
|
||||
false false and not "F and F is F." assert
|
||||
|
||||
\ Secondary words
|
||||
|
||||
: push8 8 ; push8 8 = "A word can push a number." assert
|
||||
|
||||
: push8-again push8 ;
|
||||
push8-again 8 = "A word can call another word." assert
|
||||
|
||||
: push64 push8 push8 * ;
|
||||
push64 64 = "A word can use primitive and sec words." assert
|
||||
|
||||
\ Logic
|
||||
|
||||
: 1-if-true if 1 then ;
|
||||
|
||||
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 ;
|
||||
|
||||
reset true 1-or-2 1 = "True part of ifelse fires." assert
|
||||
reset false 1-or-2 2 = "False part of ifelse fires." assert
|
||||
|
||||
\ built in functions
|
||||
|
||||
123 str "123" = "Str turns numbers into strings." assert
|
||||
"abcd" len 4 = "Len gets length of strings." 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
|
||||
|
||||
\ Lists
|
||||
|
||||
0 ->list len 0 = "->list with a lenght of 0 gives you empty list." assert
|
||||
44 1 ->list len 1 = "->list with a lenght of 1 gives you 1 list." assert
|
||||
5 7 2 ->list len 2 = "->list with a lenght of 2 gives you 2 list." assert
|
||||
|
||||
[ ] 0 ->list = "Brackets are the same as ->list." assert
|
||||
[ 88 ] 88 1 ->list = "Brackets are the same as ->list." assert
|
||||
[ 88 99 ] 99 88 2 ->list = "Brackets are the same as ->list." assert
|
||||
|
||||
[ "hello" ] first "hello" = "First works" assert
|
||||
|
||||
[ ] empty? "Empty? knows an empty list." assert
|
||||
[ 1 ] empty? not "Empty? knows a non-empty list." assert
|
||||
[ 1 2 ] empty? not "Empty? knows a non-empty list." assert
|
|
@ -36,6 +36,8 @@ class TokenStream:
|
|||
return ['eof', '']
|
||||
elif state == 'start' and ch == '\\':
|
||||
state = 'lcomment'
|
||||
elif state == 'start' and ch == '%':
|
||||
return ['word', ch]
|
||||
elif state == 'lcomment' and ch == '\n':
|
||||
state = 'start'
|
||||
elif state == 'start' and ch == '(':
|
||||
|
|
Loading…
Reference in a new issue