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
from compiler import Compiler
from arglist import Arglist
from operator_words import w_not
def const_f(value):
def x(f, i):
@ -25,11 +26,15 @@ def native_function_handler(func):
def import_native_module(forth, m, alias=None, excludes=[]):
if not alias:
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}'
localname = f'{alias}/{name}'
val = getattr(m, name)
print("setting", localname)
forth.namespace.set(localname, const_f(val))
def w_eval(f, i):
@ -62,10 +67,18 @@ def w_ns(f, i):
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[new_name] = f.namespace[old_name]
f.namespace.alias(new_name, old_name)
return i + 1
def w_require(f, i):
@ -145,11 +158,11 @@ def w_import(f, i):
def w_call(f, i):
func = f.stack.pop()
args = f.stack.pop()
# print('f', f, 'args', args)
#print('f', f, 'args', args)
try:
result = func(*args)
except:
print(f'Error executing {func}{list(args)}')
print(f'Error executing {func}({args})')
raise
# print('result', result)
f.stack.push(result)
@ -189,6 +202,9 @@ def i_semi(forth, i):
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
@ -223,6 +239,14 @@ def i_if(forth, i):
compiler.add_instruction(w_should_not_happen)
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):
compiler = forth.compiler
else_offset = compiler.pop_offset()
@ -252,26 +276,40 @@ def i_do(forth, i):
compiler.add_instruction(w_should_not_happen)
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):
compiler = forth.compiler
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
def i_until(forth, i):
def i_repeat(forth, i):
compiler = forth.compiler
while_offset = compiler.pop_offset()
begin_offset = compiler.pop_offset()
until_offset = compiler.offset()
delta = begin_offset - until_offset
#print('Delta:', delta)
compiler.instructions.append(ifnot_jump_f(delta))
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):
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
def w_dump(f, i):

View file

@ -76,8 +76,10 @@ def w_list(f, ip): # ->list
def w_thread(f, i): # @@
contents = f.stack.pop()
print("Contents:", contents)
result = contents[0]
for field in contents[1::]:
print("Result:", result)
if isinstance(field, str) and hasattr(result, field):
result = getattr(result, field) # result.field
elif isinstance(field, Arglist):

View file

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

View file

@ -1,12 +1,12 @@
: open builtins.open !!1 ;
: open builtins/open !!1 ;
: close <. $? 'close .> !!0 drop ;
: read-file (path -- contents) open dup <. $? 'read .> !!0 swap close ;
: read-lines (path -- contents) open dup <. $? 'readlines .> !!0 swap close ;
: read-line (prompt -- input-line)
builtins.input !!1
builtins/input !!1
;
\ : 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
from basic_words import const_f, w_enlist
import tokenstream as ts
from kword import Keyword
from stack import Stack
from namespace import Namespace
def to_number(token):
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
return None
class Forth:
def __init__(self, startup=None):
self.streams = Stack()
@ -39,6 +31,7 @@ class Forth:
self.forth_ns.import_from_module(operator_words)
self.forth_ns.import_from_module(stack_words)
self.forth_ns.import_from_module(os_words)
self.namespace.alias("*execute-command*", "execute")
self.compiler = None
@ -58,77 +51,71 @@ class Forth:
def compiling(self):
return self.compiler
def _compile_token(self, kind, token):
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
self.compiler.name = token.value
return
if kind in ['dqstring', 'sqstring']:
self.compiler.add_instruction(const_f(token))
if token.isnumber() or token.isstring():
self.compiler.add_instruction(const_f(token.value))
return
if token in self.namespace:
entry = self.namespace[token]
#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)
if token.iskeyword():
self.compiler.add_instruction(Keyword(token.value))
return
n = to_number(token)
if n == None:
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:
self.compiler.add_instruction(const_f(n))
value = entry.get_cvalue()
self.compiler.add_instruction(value)
def _eval_token(self, kind, token):
#print(f'eval token {token} kind {kind}')
if kind in ['dqstring', 'sqstring']:
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:
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:
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}')
kts = self.macro_expand_token(kind, token)
#print(kts)
for kt in kts:
this_kind, this_token = kt
expanded_tokens = self.macro_expand_token(token)
#print(expanded_tokens)
for expanded in expanded_tokens:
if not self.compiling():
#print("interactive", this_token)
self._eval_token(this_kind, this_token)
self._eval_token(expanded)
else:
#print("compiling...", this_token)
self._compile_token(this_kind, this_token)
#print("Done")
self._compile_token(expanded)
def execute_current_stream(self):
s = self.streams.peek()
#print("exec current s:", s)
kind, token = s.get_token()
while kind != 'eof':
self.execute_token(kind, token)
kind, token = s.get_token()
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):
@ -140,6 +127,22 @@ class Forth:
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()
@ -154,24 +157,29 @@ class Forth:
rargs.reverse()
if rargs:
for a in rargs:
# print("pushing", a);
#print("pushing", a);
self.stack.push(a)
#print(f'Before eval stack is {str(self.stack)}')
return self.evaluate_string(s)
def macro_expand_token(self, kind, token):
if len(token) <= 0 or token[0] != '#':
return [[kind, token]]
def macro_expand_token(self, token):
if not token.isword():
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('.')
result = [ '<.', parts[0] ]
result = [['word', '<.'], ['word', parts[0]]]
print("Parts", parts)
result = [ ts.Token('word', '<.'), ts.Token('word', parts[0]) ]
for part in parts[1::]:
result.append(['sqstring', part])
result.append(['word', '.>'])
#print(result)
result.append(ts.Token('string', part))
result.append(ts.Token('word', '.>'))
print(result)
return result
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)
swap
2 ->list
builtins.slice
builtins/slice
!!
;
@ -25,11 +25,11 @@
[x]
;
: repeat (n x -- list-of-x-repeated-n-times)
: n-of (n x -- list-of-x-repeated-n-times)
1 ->list *
;
: len builtins.len !!1 ;
: len builtins/len !!1 ;
: empty? len zero? ;
@ -39,3 +39,9 @@
: ffirst (list -- first-of-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.refers = refers.copy()
def alias(self, new_name, existing_name):
self.contents[new_name] = self.contents[existing_name]
def refer(self, ns):
"""
Add the supplied namespace to the refers list.

View file

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

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
[ 1 ] 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