Clean up forth eval/execute, make repl more forth driven.

This commit is contained in:
Russ Olsen 2020-05-01 08:06:08 -04:00
parent 62acb4ab95
commit a956438aec
11 changed files with 214 additions and 59 deletions

View file

@ -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

View file

@ -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)

View file

@ -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 ;

View file

@ -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):

View file

@ -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()

View file

@ -44,16 +44,21 @@ def setup_forth():
def repl(f):
while True:
try:
prompt = f.evaluate_string('*prompt*')
try:
line = input(prompt)
line += "\n"
except EOFError:
return
try:
f.execute_string(line)
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()

View file

@ -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

View file

@ -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):

View file

@ -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
View 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

View file

@ -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 == '(':