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) val = getattr(m, name)
forth.namespace.set(localname, const_f(val)) 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): def w_eval(f, i):
token = f.stack.pop() 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 return i+1
def w_no_op(f, i): def w_no_op(f, i):
@ -155,6 +155,20 @@ def w_call(f, i):
f.stack.push(result) f.stack.push(result)
return i+1 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): def w_nl(f, i):
print() print()
return i+1 return i+1
@ -165,11 +179,16 @@ def w_return(f, i):
def w_colon(f, i): def w_colon(f, i):
f.compiler = Compiler() f.compiler = Compiler()
def i_inline(f, i):
f.compiler.inline = True
def i_semi(forth, i): def i_semi(forth, i):
forth.compiler.add_instruction(w_return) forth.compiler.add_instruction(w_return)
name = forth.compiler.name name = forth.compiler.name
word_f = execute_f(name, forth.compiler.instructions) 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 forth.compiler = None
return i+1 return i+1
@ -277,4 +296,7 @@ def w_enlist(f, i):
f.stack.push([x]) f.stack.push([x])
return i+1 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.name = name
self.instructions = [] self.instructions = []
self.offsets = Stack() self.offsets = Stack()
self.inline = False
def add_instruction(self, ins): def add_instruction(self, ins):
self.instructions.append(ins) self.instructions.append(ins)
def add_instructions(self, instructions):
self.instructions.extend(instructions)
def offset(self): def offset(self):
return len(self.instructions) return len(self.instructions)

View file

@ -36,9 +36,9 @@
: prompt-and-run ( -- prog-status) : prompt-and-run ( -- prog-status)
">> " read-line ">> " read-line
dup "x" = dup "q" =
if if
"Exit!" p "Quit!" p
drop drop
else else
tokenize tokenize
@ -47,3 +47,5 @@
recur recur
then then
; ;
: % "Toggle" p ;

View file

@ -50,10 +50,10 @@ class Forth:
self.namespace = user_ns self.namespace = user_ns
def defword(self, name, value): def defword(self, name, value):
self.namespace.set(name, value) return self.namespace.set(name, value)
def defvar(self, name, value): def defvar(self, name, value):
self.defword(name, const_f(value)) return self.defword(name, const_f(value))
def compiling(self): def compiling(self):
return self.compiler return self.compiler
@ -61,7 +61,7 @@ class Forth:
def _compile_token(self, kind, token): def _compile_token(self, kind, token):
#print(f"compile: {self.compiler.name}: {token}") #print(f"compile: {self.compiler.name}: {token}")
if self.compiler.name == None: if self.compiler.name == None:
print(f'Compiling {token}') #print(f'Compiling {token}')
self.compiler.name = token self.compiler.name = token
return return
@ -75,6 +75,8 @@ class Forth:
if entry.immediate: if entry.immediate:
value = entry.get_ivalue() value = entry.get_ivalue()
value(self, 0) value(self, 0)
elif entry.inline:
self.compiler.add_instructions(entry.definition[slice(0,-1)])
else: else:
value = entry.get_cvalue() value = entry.get_cvalue()
self.compiler.add_instruction(value) self.compiler.add_instruction(value)
@ -82,12 +84,13 @@ class Forth:
n = to_number(token) n = to_number(token)
if n == None: if n == None:
print(f'{token}? Compile of {self.compiler.name} terminated.') print(f'[{token}]?? Compile of [{self.compiler.name}] terminated.')
self.compiler = None self.compiler = None
else: else:
self.compiler.add_instruction(const_f(n)) self.compiler.add_instruction(const_f(n))
def _eval_token(self, kind, token): def _eval_token(self, kind, token):
#print(f'eval token {token} kind {kind}')
if kind in ['dqstring', 'sqstring']: if kind in ['dqstring', 'sqstring']:
self.stack.push(token) self.stack.push(token)
return return
@ -153,7 +156,7 @@ class Forth:
for a in rargs: for a in rargs:
# print("pushing", a); # print("pushing", a);
self.stack.push(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) return self.evaluate_string(s)
@ -168,7 +171,7 @@ class Forth:
for part in parts[1::]: for part in parts[1::]:
result.append(['sqstring', part]) result.append(['sqstring', part])
result.append(['word', '.>']) result.append(['word', '.>'])
print(result) #print(result)
return result return result
def set_ns(self, ns_name): def set_ns(self, ns_name):

View file

@ -1,8 +1,10 @@
class Entry: class Entry:
def __init__(self, name, value, immediate): def __init__(self, name, value, immed):
self.name = name self.name = name
self.value = value self.value = value
self.immediate = immediate self.immediate = immed
self.inline = False
self.definition = None
def get_ivalue(self): def get_ivalue(self):
return self.value return self.value
@ -11,7 +13,10 @@ class Entry:
return self.value return self.value
def __str__(self): 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: class Namespace:
def __init__(self, name, initial_contents={}, refers=[]): def __init__(self, name, initial_contents={}, refers=[]):
@ -51,6 +56,7 @@ class Namespace:
entry.cvalue = cvalue entry.cvalue = cvalue
entry.immediate = immediate entry.immediate = immediate
self.contents[name] = entry self.contents[name] = entry
return entry
def keys(self): def keys(self):
return self.contents.keys() return self.contents.keys()

View file

@ -44,16 +44,21 @@ def setup_forth():
def repl(f): def repl(f):
while True: while True:
prompt = f.evaluate_string('*prompt*')
try: try:
line = input(prompt) prompt = f.evaluate_string('*prompt*')
except EOFError: try:
return line = input(prompt)
try: line += "\n"
f.execute_string(line) except EOFError:
except: return
traceback.print_exc() try:
f.stack.push(line)
f.execute_string("*execute-command*")
#f.execute_string(line)
except:
traceback.print_exc()
except KeyboardInterrupt:
print()
if __name__ == "__main__": if __name__ == "__main__":
f = setup_forth() f = setup_forth()

View file

@ -23,6 +23,9 @@ class Stack:
for i in range(0, self.top+1): for i in range(0, self.top+1):
yield self.stack[i] yield self.stack[i]
def depth(self):
return self.top + 1
def empty(self): def empty(self):
return self.top == -1 return self.top == -1

View file

@ -16,6 +16,10 @@ def w_dot(f, i):
print(a, end='') print(a, end='')
return i+1 return i+1
def w_stackdepth(f, i):
d = f.stack.depth()
f.stack.push(d)
def w_splat(f, i): def w_splat(f, i):
l = f.stack.pop() l = f.stack.pop()
l.reverse() l.reverse()
@ -29,12 +33,6 @@ def w_dup(f, i):
return i+1 return i+1
def w_tmb(f, i): # A noop 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 return i+1
def w_tbm(f, i): def w_tbm(f, i):

View file

@ -11,6 +11,8 @@
"io" require "io" require
"time" require "time" require
'builtins import
\ Basic aliases \ Basic aliases
'None 'nil alias 'None 'nil alias
@ -36,13 +38,14 @@
'current_ns '*ns* alias 'current_ns '*ns* alias
: *prompt* "Sally> " ; : *prompt* "Sally> " ;
: *execute-command* stack execute ;
\ Make a list. \ Make a list.
'list-marker unique def 'list-marker unique def
: [ list-marker ; : [ list-marker inline ;
: ] list-marker [list] ; : ] list-marker [list] inline ;
: [] ( -- <empty list>) [ ] ; : [] ( -- <empty list>) [ ] inline ;
\ Look up attributes on a value. \ Look up attributes on a value.
@ -51,16 +54,16 @@
: $? swap ; : $? swap ;
\ Call native functions with various # arguments. \ Call native functions with various # arguments.
: !!0 [] swap !! ; : !!0 [] swap !! inline ;
: !!1 swap 1 ->list swap !! ; : !!1 swap 1 ->list swap !! inline ;
: !!2 swap 2 ->list swap !! ; : !!2 swap 2 ->list swap !! inline ;
\ Make a map. \ Make a map.
'map-marker unique def 'map-marker unique def
: { map-marker ; : { map-marker inline ;
: } map-marker [list] list->map ; : } map-marker [list] list->map inline ;
: {} ( -- <empty map>) { } ; : {} ( -- <empty map>) { } inline ;
\ Make a set. \ Make a set.
@ -71,16 +74,21 @@
set-marker [list] \ Turn elements into list set-marker [list] \ Turn elements into list
set-marker swap set-marker [list] \ Nest list in argument list set-marker swap set-marker [list] \ Nest list in argument list
builtins.set !! \ Call set with 1 argument builtins.set !! \ Call set with 1 argument
inline
; ;
: {{}} ( -- <empty set>) {{ }} ; : {{}} ( -- <empty set>) {{ }} inline ;
: [: [ ; : [: [ inline ;
: :] ] ->arglist ; : :] ] ->arglist inline ;
: str builtins.str !!1 ;
: type builtins.type !!1 ; : type builtins.type !!1 ;
: type? (x class -- bool) swap type = ;
: ctime time.ctime !!0 ; : ctime time.ctime !!0 ;
: sleep time.sleep !!1 drop ; : sleep time.sleep !!1 drop ;
@ -89,22 +97,22 @@
: hello "Hello" . nl ; : hello "Hello" . nl ;
: >0 0 > ; : >0 0 > inline ;
: =0 0 = ; : =0 0 = inline ;
: <1 1 < ; : <1 1 < inline ;
: <0 0 < ; : <0 0 < inline ;
: >1 1 > ; : >1 1 > inline ;
: <1 1 < ; : <1 1 < inline ;
: p . nl ; : p . nl ;
: top dup p ; : top dup p ;
: -- -1 + ; : -- -1 + inline ;
: ++ 1 + ; : ++ 1 + inline ;
: *2 2 * ; : *2 2 * inline ;
: pos? 0 > ; : pos? 0 > inline ;
: neg? 0 < ; : neg? 0 < inline ;
: zero? 0 = ; : zero? 0 = inline ;
: exists? os.path.exists !!1 ; : exists? os.path.exists !!1 ;
@ -123,7 +131,16 @@
: .!!2 (obj a1 a2 method-name -- result ) swap 2 ->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 .!! ; : .!!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 "string.sf" source
"list.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', ''] return ['eof', '']
elif state == 'start' and ch == '\\': elif state == 'start' and ch == '\\':
state = 'lcomment' state = 'lcomment'
elif state == 'start' and ch == '%':
return ['word', ch]
elif state == 'lcomment' and ch == '\n': elif state == 'lcomment' and ch == '\n':
state = 'start' state = 'start'
elif state == 'start' and ch == '(': elif state == 'start' and ch == '(':