mirror of
https://github.com/russolsen/sallyforth
synced 2024-12-25 21:58:18 +01:00
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:
parent
4473e149c3
commit
e488738143
11 changed files with 186 additions and 104 deletions
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 ;
|
||||
|
|
|
@ -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 ;
|
||||
|
|
|
@ -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,20 +51,28 @@ 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 token.iskeyword():
|
||||
self.compiler.add_instruction(Keyword(token.value))
|
||||
return
|
||||
|
||||
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)
|
||||
|
@ -80,55 +81,41 @@ class Forth:
|
|||
else:
|
||||
value = entry.get_cvalue()
|
||||
self.compiler.add_instruction(value)
|
||||
return
|
||||
|
||||
n = to_number(token)
|
||||
if n == None:
|
||||
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
|
||||
|
||||
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
12
sallyforth/kword.py
Normal 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)
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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 ;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue