Add :keywords, if-not, begin-while-repeat. Change names of values imported from native python modules from mod1.mod2.value to mod1/mod2/value.

This commit is contained in:
Russ Olsen 2020-05-02 10:45:07 -04:00
parent 4473e149c3
commit e488738143
11 changed files with 186 additions and 104 deletions

View file

@ -3,6 +3,7 @@ import importlib
import os import os
from compiler import Compiler from compiler import Compiler
from arglist import Arglist from arglist import Arglist
from operator_words import w_not
def const_f(value): def const_f(value):
def x(f, i): def x(f, i):
@ -25,11 +26,15 @@ def native_function_handler(func):
def import_native_module(forth, m, alias=None, excludes=[]): def import_native_module(forth, m, alias=None, excludes=[]):
if not alias: if not alias:
alias = m.__name__ alias = m.__name__
alias = alias.replace(".", "/")
print(m, alias)
raw_names = dir(m) raw_names = dir(m)
names = [x for x in raw_names if x not in excludes] names = [x for x in raw_names if x not in excludes]
for name in names: for name in names:
localname = f'{alias}.{name}' localname = f'{alias}/{name}'
val = getattr(m, name) val = getattr(m, name)
print("setting", localname)
forth.namespace.set(localname, const_f(val)) forth.namespace.set(localname, const_f(val))
def w_eval(f, i): def w_eval(f, i):
@ -62,10 +67,18 @@ def w_ns(f, i):
f.namespace = new_ns f.namespace = new_ns
return i + 1 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): def w_alias(f, i):
new_name = f.stack.pop() new_name = f.stack.pop()
old_name = f.stack.pop() old_name = f.stack.pop()
f.namespace[new_name] = f.namespace[old_name] f.namespace.alias(new_name, old_name)
return i + 1 return i + 1
def w_require(f, i): def w_require(f, i):
@ -145,11 +158,11 @@ def w_import(f, i):
def w_call(f, i): 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)
try: try:
result = func(*args) result = func(*args)
except: except:
print(f'Error executing {func}{list(args)}') print(f'Error executing {func}({args})')
raise raise
# print('result', result) # print('result', result)
f.stack.push(result) f.stack.push(result)
@ -189,6 +202,9 @@ def i_semi(forth, i):
entry = forth.defword(name, word_f) entry = forth.defword(name, word_f)
entry.inline = forth.compiler.inline entry.inline = forth.compiler.inline
entry.definition = forth.compiler.instructions entry.definition = forth.compiler.instructions
#print(name)
#for ins in entry.definition:
# print(ins)
forth.compiler = None forth.compiler = None
return i+1 return i+1
@ -223,6 +239,14 @@ def i_if(forth, i):
compiler.add_instruction(w_should_not_happen) compiler.add_instruction(w_should_not_happen)
return i+1 return i+1
def i_ifnot(forth, i):
compiler = forth.compiler
compiler.add_instruction(w_not)
compiler.push_offset()
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+2
def i_then(forth, i): def i_then(forth, i):
compiler = forth.compiler compiler = forth.compiler
else_offset = compiler.pop_offset() else_offset = compiler.pop_offset()
@ -252,26 +276,40 @@ def i_do(forth, i):
compiler.add_instruction(w_should_not_happen) compiler.add_instruction(w_should_not_happen)
return i+1 return i+1
def i_while(forth, i):
compiler = forth.compiler
do_offset = compiler.pop_offset()
while_offset = compiler.offset()
delta = do_offset - while_offset
compiler.instructions[if_offset] = ifnot_jump_f(delta)
return i+1
def i_begin(forth, i): def i_begin(forth, i):
compiler = forth.compiler compiler = forth.compiler
compiler.push_offset() compiler.push_offset()
return i
def i_while(forth, i):
compiler = forth.compiler
compiler.push_offset()
compiler.add_instruction(w_should_not_happen)
return i+1 return i+1
def i_until(forth, i): def i_repeat(forth, i):
compiler = forth.compiler compiler = forth.compiler
while_offset = compiler.pop_offset()
begin_offset = compiler.pop_offset() begin_offset = compiler.pop_offset()
until_offset = compiler.offset() repeat_offset = compiler.offset()
delta = begin_offset - until_offset begin_delta = begin_offset - repeat_offset
#print('Delta:', delta) while_delta = repeat_offset - while_offset + 1
compiler.instructions.append(ifnot_jump_f(delta)) 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):
print("marker1")
return i+1
def w_marker2(f, i):
print("marker3")
return i+1
def w_marker3(f, i):
print("marker3")
return i+1 return i+1
def w_dump(f, i): def w_dump(f, i):

View file

@ -76,8 +76,10 @@ def w_list(f, ip): # ->list
def w_thread(f, i): # @@ def w_thread(f, i): # @@
contents = f.stack.pop() contents = f.stack.pop()
print("Contents:", contents)
result = contents[0] result = contents[0]
for field in contents[1::]: for field in contents[1::]:
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): elif isinstance(field, Arglist):

View file

@ -28,8 +28,8 @@
'os import 'os import
: expandvars #os.path.expandvars !!1 ; : expandvars #os/path/expandvars !!1 ;
: expanduser #os.path.expanduser !!1 ; : expanduser #os/path/expanduser !!1 ;
: expand expanduser expandvars ; : expand expanduser expandvars ;
: tokenize <. $? 'split .> " " swap !!1 ; : tokenize <. $? 'split .> " " swap !!1 ;
@ -48,4 +48,5 @@
then then
; ;
: % "Toggle" p ; : % "Toggle" p ;

View file

@ -1,12 +1,12 @@
: 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 ; \ : read-next nexttoken second ;

View file

@ -3,18 +3,10 @@ from os import path
import basic_words, data_words, operator_words, stack_words, os_words import basic_words, data_words, operator_words, stack_words, os_words
from basic_words import const_f, w_enlist from basic_words import const_f, w_enlist
import tokenstream as ts import tokenstream as ts
from kword import Keyword
from stack import Stack from stack import Stack
from namespace import Namespace from namespace import Namespace
def to_number(token):
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
return None
class Forth: class Forth:
def __init__(self, startup=None): def __init__(self, startup=None):
self.streams = Stack() self.streams = Stack()
@ -39,6 +31,7 @@ class Forth:
self.forth_ns.import_from_module(operator_words) self.forth_ns.import_from_module(operator_words)
self.forth_ns.import_from_module(stack_words) self.forth_ns.import_from_module(stack_words)
self.forth_ns.import_from_module(os_words) self.forth_ns.import_from_module(os_words)
self.namespace.alias("*execute-command*", "execute")
self.compiler = None self.compiler = None
@ -58,77 +51,71 @@ class Forth:
def compiling(self): def compiling(self):
return self.compiler return self.compiler
def _compile_token(self, kind, token): def _compile_token(self, 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.value
return return
if kind in ['dqstring', 'sqstring']: if token.isnumber() or token.isstring():
self.compiler.add_instruction(const_f(token)) self.compiler.add_instruction(const_f(token.value))
return return
if token in self.namespace: if token.iskeyword():
entry = self.namespace[token] self.compiler.add_instruction(Keyword(token.value))
#print(token, entry)
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)
return return
n = to_number(token) if token.value not in self.namespace:
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
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: else:
self.compiler.add_instruction(const_f(n)) value = entry.get_cvalue()
self.compiler.add_instruction(value)
def _eval_token(self, kind, token): def _eval_token(self, token):
#print(f'eval token {token} kind {kind}') #print(f'***Eval token {token}')
if kind in ['dqstring', 'sqstring']: if token == None:
self.stack.push(token)
return
if token in self.namespace:
#print("executing ", token)
f = self.namespace[token].get_ivalue()
#print(f)
f(self, 0)
return
n = to_number(token)
if n == None:
print(f'{token}?') 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: else:
self.stack.push(n) entry = self.namespace[token.value]
f = entry.get_ivalue()
f(self, 0)
def execute_token(self, kind, token): def execute_token(self, token):
#print(f'execute_token: {token}') #print(f'execute_token: {token}')
kts = self.macro_expand_token(kind, token) expanded_tokens = self.macro_expand_token(token)
#print(kts) #print(expanded_tokens)
for kt in kts: for expanded in expanded_tokens:
this_kind, this_token = kt
if not self.compiling(): if not self.compiling():
#print("interactive", this_token) self._eval_token(expanded)
self._eval_token(this_kind, this_token)
else: else:
#print("compiling...", this_token) self._compile_token(expanded)
self._compile_token(this_kind, this_token)
#print("Done")
def execute_current_stream(self): def execute_current_stream(self):
s = self.streams.peek() s = self.streams.peek()
#print("exec current s:", s) #print("exec current s:", s)
kind, token = s.get_token() token = s.get_token()
while kind != 'eof': while token:
self.execute_token(kind, token) #print("Exec:", token)
kind, token = s.get_token() self.execute_token(token)
token = s.get_token()
self.streams.pop() self.streams.pop()
def execute_token_stream(self, s): def execute_token_stream(self, s):
@ -140,6 +127,22 @@ class Forth:
token_stream = ts.string_token_stream(s) token_stream = ts.string_token_stream(s)
return self.execute_token_stream(token_stream) 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): def evaluate_string(self, s):
self.execute_string(s) self.execute_string(s)
return self.stack.pop() return self.stack.pop()
@ -154,24 +157,29 @@ class Forth:
rargs.reverse() rargs.reverse()
if rargs: if rargs:
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)
def macro_expand_token(self, kind, token): def macro_expand_token(self, token):
if len(token) <= 0 or token[0] != '#': if not token.isword():
return [[kind, token]] return [token]
elif len(token.value) <= 1:
return [token]
elif token.value[0] != '#':
return [token]
tag = token[1:] print("Expanding token:", token)
tag = token.value[1:]
parts = tag.split('.') parts = tag.split('.')
result = [ '<.', parts[0] ] print("Parts", parts)
result = [['word', '<.'], ['word', parts[0]]] result = [ ts.Token('word', '<.'), ts.Token('word', parts[0]) ]
for part in parts[1::]: for part in parts[1::]:
result.append(['sqstring', part]) result.append(ts.Token('string', part))
result.append(['word', '.>']) result.append(ts.Token('word', '.>'))
#print(result) print(result)
return result return result
def set_ns(self, ns_name): def set_ns(self, ns_name):

12
sallyforth/kword.py Normal file
View file

@ -0,0 +1,12 @@
from collections import UserString
class Keyword(UserString):
def __init__(self, value):
value = value[1::]
UserString.__init__(self, value)
def __call__(self, d):
return d[self]
def __repr__(self):
return ':' + str(self)

View file

@ -11,7 +11,7 @@
: slice (start stop -- slice-obj) : slice (start stop -- slice-obj)
swap swap
2 ->list 2 ->list
builtins.slice builtins/slice
!! !!
; ;
@ -25,11 +25,11 @@
[x] [x]
; ;
: repeat (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? ;
@ -39,3 +39,9 @@
: 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)
dup tbm
<. $? 'append .> !!1
drop
;

View file

@ -24,6 +24,9 @@ class Namespace:
self.contents = initial_contents.copy() self.contents = initial_contents.copy()
self.refers = refers.copy() self.refers = refers.copy()
def alias(self, new_name, existing_name):
self.contents[new_name] = self.contents[existing_name]
def refer(self, ns): def refer(self, ns):
""" """
Add the supplied namespace to the refers list. Add the supplied namespace to the refers list.

View file

@ -38,7 +38,7 @@
'current_ns '*ns* alias 'current_ns '*ns* alias
: *prompt* "Sally> " ; : *prompt* "Sally> " ;
: *execute-command* stack execute ; : *execute-command* execute ;
\ Make a list. \ Make a list.
'list-marker unique def 'list-marker unique def
@ -73,7 +73,7 @@
: }} : }}
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 inline
; ;
@ -83,17 +83,17 @@
: :] ] ->arglist inline ; : :] ] ->arglist inline ;
: str builtins.str !!1 ; : str builtins/str !!1 ;
: type builtins.type !!1 ; : type builtins/type !!1 ;
: type? (x class -- bool) swap type = ; : type? (x class -- bool) swap type = ;
: ctime time.ctime !!0 ; : ctime time/ctime !!0 ;
: sleep time.sleep !!1 drop ; : sleep time/sleep !!1 drop ;
: callable? builtins.callable !!1 ; : callable? builtins/callable !!1 ;
: hello "Hello" . nl ; : hello "Hello" . nl ;
@ -114,16 +114,18 @@
: neg? 0 < inline ; : neg? 0 < inline ;
: zero? 0 = inline ; : zero? 0 = inline ;
: exists? os.path.exists !!1 ; : exists? os/path/exists !!1 ;
: source-if-exists : source-if-exists
(path -- result-of-sourcing) (path -- result-of-sourcing)
"SOURCE IF EXISTS" p
stack
dup dup
exists? exists?
if source else drop then if source else drop then
; ;
: getattr ( obj attr -- attr-value ) swap 2 ->list builtins.getattr !! ; : getattr ( obj attr -- attr-value ) swap 2 ->list builtins/getattr !! ;
: .!! (obj args method-name -- result) tbm getattr !! ; : .!! (obj args method-name -- result) tbm getattr !! ;
: .!!0 (obj method-name -- result ) [] swap .!! ; : .!!0 (obj method-name -- result ) [] swap .!! ;
@ -132,11 +134,11 @@
: .!!3 (obj a1 a2 a3 method-name -- result ) swap 3 ->list swap .!! ; : .!!3 (obj a1 a2 a3 method-name -- result ) swap 3 ->list swap .!! ;
: assert ( bool msg -- ) : assert ( bool msg -- )
swap p
if if
"OK " . p "OK " p
else else
#builtins.AssertionError !!1 #builtins/AssertionError !!1
raise raise
then then
; ;

View file

@ -1,4 +1,4 @@
: 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 ;

View file

@ -91,3 +91,13 @@ reset false 1-or-2 2 = "False part of ifelse fires." assert
[ ] empty? "Empty? knows an empty list." assert [ ] empty? "Empty? knows an empty list." assert
[ 1 ] empty? not "Empty? knows a non-empty list." assert [ 1 ] empty? not "Empty? knows a non-empty list." assert
[ 1 2 ] empty? not "Empty? knows a non-empty list." assert [ 1 2 ] empty? not "Empty? knows a non-empty list." assert
\ Loop
: test-while ( n -- ) -999 swap begin dup zero? while -- repeat -888 ;
5 test-while 3 ->list [ -999 0 -888 ] "While loop works" assert
: zero-trip-while begin false while "Should not get here." repeat ;
888 zero-trip-while 888 = "While should handle zero trip case." assert