1
from __future__ import print_function
2
from __future__ import division
7
from functools import partial
14
if sys.version_info[0] < 3:
17
sys.path.append("../shell")
23
def setimmediate(func):
24
func.is_immediate = True
29
return array.array('B', [ord(c) for c in x])
30
elif type(x) == bytes:
31
return array.array('B', x)
33
return array.array('B', str(x))
36
if hasattr(array.array, 'tostring'):
41
class ForthException(Exception):
42
def __init__(self, value):
47
def __init__(self, CELL = 4, ENDIAN = '<'):
48
self.d = [] # data stack
49
self.r = [] # return stack
50
self.dict = {} # the dictionary
51
self.xts = [] # execution token (xt) table
52
self.ip = 0 # instruction pointer for inner interpreter
53
self.loopC = 0 # loop count
54
self.loopL = 0 # loop limit
55
self.leaves = [] # tracking LEAVEs from DO..LOOP
56
self.ram = array.array('B') # memory
57
self.out = sys.stdout.write # default console output
60
self.CSIGN = (256 ** self.CELL) >> 1 # Sign bit mask
61
self.CMASK = (256 ** self.CELL) - 1 # Cell mask
62
self.cellfmt = ENDIAN + {2: 'h', 4: 'i', 8: 'q'}[self.CELL]
65
r = partial(self.lit, len(self.ram))
67
self.ram.extend([0] * n)
70
self.tib = allot(256, "TIB")
71
self.sourcea = allot(self.CELL, "SOURCEA")
72
self.sourcec = allot(self.CELL, "SOURCEC")
73
self.sourceid = allot(self.CELL, "SOURCEID")
74
self.to_in = allot(self.CELL, ">IN")
75
self.base = allot(self.CELL, "BASE")
76
self.state = allot(self.CELL, "STATE")
78
# Run through own bound methods, adding each to the dict
79
isforth = re.compile(r"[A-Z0-9<>=\-\[\],@!:;+?/*]+$")
80
for name in dir(self):
81
o = getattr(self, name)
82
if not isforth.match(name) and o.__doc__:
83
# name was not a valid Forth name; try start of the docstring
84
name = o.__doc__.split()[0]
85
if callable(o) and isforth.match(name):
100
""" push literal N on the stack """
115
def binary(self, op):
117
self.d[-1] = self.w32(op(self.d[-1], b))
120
r = self.d.pop() << (8 * self.CELL)
121
r += self.d.pop() & self.CMASK
125
self.lit(self.w32(d & self.CMASK))
126
self.lit(self.w32(d >> (8 * self.CELL)))
131
return array2bytes(self.ram[a:a+n]).decode("utf-8")
133
# Start of Forth words
135
# If the word is a legal Python identifier, then
136
# use that name. Otherwise (e.g. '+') the Forth name is in
140
self.lit(len(self.ram))
145
raise ForthException(e)
148
self.q('SOURCEA @ SOURCEC @ >IN @')
149
self.q('SOURCEID @ >R')
150
source_spec = self.popn(3)
151
(ds,rs,ip) = (len(self.d) - 1, len(self.r), self.ip)
154
except ForthException as e:
158
self.d = self.d + [0] * (ds - len(self.d))
161
self.lit(source_spec[0])
162
self.lit(source_spec[1])
163
self.lit(source_spec[2])
164
self.q('R> SOURCEID !')
165
self.q('>IN ! SOURCEC ! SOURCEA !')
172
self.d[-1] += self.CELL
175
self.lit(len(self.d))
186
self.lit(*struct.unpack(self.cellfmt, self.ram[a:a + self.CELL]))
191
self.lit(self.ram[a])
197
self.ram[a:a + self.CELL] = array.array('B', struct.pack(self.cellfmt, x))
203
self.ram[a] = x & 0xff
207
self.ram.extend(ba(struct.pack(self.cellfmt, self.d.pop())))
211
self.ram.extend([self.d.pop()])
213
def slash_string(self):
221
self.q('SOURCE >IN @ /STRING')
227
if (self.ram[self.d[-2]]) == delim:
232
self.q('2DUP 1 MIN + SOURCE DROP - >IN !')
233
self.q('DROP R> TUCK -')
235
def parse_name(self):
237
self.q('SOURCE >IN @ /STRING')
243
if not pred(self.ram[self.d[-2]]):
248
skip(lambda x: x == 32)
250
skip(lambda x: x != 32)
252
self.q('2DUP 1 MIN + SOURCE DROP - >IN !')
253
self.q('DROP R> TUCK -')
256
self.d.append(self.d[-1])
270
(self.d[-2], self.d[-1]) = (self.d[-1], self.d[-2])
274
(self.d[-4], self.d[-3], self.d[-2], self.d[-1]) = (self.d[-2], self.d[-1], self.d[-4], self.d[-3])
290
self.d += self.d[-2:]
294
self.r.append(self.d.pop())
298
self.d.append(self.r.pop())
302
self.d.append(self.r[-1])
308
self.r += self.d[-n:]
316
self.d += self.r[-n:]
322
self.binary(operator.__add__)
326
self.binary(operator.__sub__)
330
self.binary(operator.__and__)
334
self.binary(operator.__or__)
338
self.binary(operator.__xor__)
341
self.binary(operator.__lshift__)
344
self.binary(lambda a, b: (a & self.CMASK) >> b)
352
self.binary(lambda a, b: truth(a == b))
356
self.binary(lambda a, b: truth(a < b))
358
def u_less_than(self):
360
self.binary(lambda a, b: truth((a & self.CMASK) < (b & self.CMASK)))
363
self.d[-1] = self.w32(-self.d[-1])
366
self.d[-1] = self.w32(self.d[-1] ^ self.CMASK)
369
self.lit(min(self.d.pop(), self.d.pop()))
372
self.lit(max(self.d.pop(), self.d.pop()))
376
self.dlit(self.dpop() + self.dpop())
380
self.dlit(self.u32(self.d.pop()) * self.u32(self.d.pop()))
384
self.binary(operator.__mul__)
386
def u_m_slash_mod(self):
388
u1 = self.u32(self.d.pop())
389
ud = self.dpop() & (65536**self.CELL - 1)
390
self.lit(self.w32(ud % u1))
391
self.lit(self.w32(ud // u1))
394
time.sleep(0.001 * self.d.pop())
397
self.out(chr(self.d.pop()))
411
self.out(" ".join(self.dict))
414
if not c in self.xts:
416
return self.xts.index(c) + 1000
420
s = array2bytes(self.ram[a:a+n]).decode("utf-8").upper()
421
# print('HERE', s.decode("utf-8"), self.dict)
424
self.d[-2] = self.xt(x)
425
if hasattr(x, 'is_immediate'):
437
def left_paren(self):
443
def right_paren(self):
449
def inner(self, code):
452
while self.ip < len(code):
460
name = self.pops().upper()
461
def restore(here, dict):
464
self.dict[name] = partial(restore, len(self.ram), copy.copy(self.dict))
469
self.defining = self.pops().upper()
476
self.lastword = partial(self.inner, self.code)
477
if self.defining in self.dict:
478
print('warning: refining %s' % self.defining)
479
self.dict[self.defining] = self.lastword
480
self.dosemi = endcolon
490
self.code.append(partial(self.inner, self.code))
497
self.lit(self.xt(partial(self.inner, self.code)))
498
self.dosemi = endnoname
501
setattr(self.lastword, 'is_immediate', True)
508
self.code.append(partial(self.inner, code))
510
self.code.append(partial(dodoes, dobody))
514
self.dosemi = lambda: 0
518
code = self.xts[self.d.pop() - 1000].args[0]
523
self.ram.extend(ba(chr(0) * self.d.pop()))
531
assert 0, "Bad postpone %s" % self.pops()
534
self.lit(self.xt(self.compile_comma))
541
(a, n) = self.popn(2)
544
self.ram[a:a + ns] = s
547
def to_number(self, base = None):
554
(a, n) = self.popn(2)
558
ud2 = base * ud2 + int(chr(self.ram[a]), base)
572
def compile_comma(self):
574
self.code.append(self.xts[self.d.pop() - 1000])
579
def zbranch(self, x):
580
if self.d.pop() == 0:
585
self.lit(len(self.code))
589
self.code.append(partial(self.branch, self.d.pop()))
593
self.lit(len(self.code))
594
self.code.append(self.branch)
599
self.lit(len(self.code))
600
self.code.append(self.zbranch)
605
self.code[p] = partial(self.code[p], len(self.code))
609
self.code.append(partial(self.zbranch, self.d.pop()))
613
self.code.append(partial(self.lit, self.d.pop()))
616
self.r.append(self.loopC)
617
self.r.append(self.loopL)
618
self.loopC = self.d.pop()
619
self.loopL = self.d.pop()
622
self.r.append(self.loopC)
623
self.r.append(self.loopL)
624
self.loopC = self.d[-1]
625
self.loopL = self.d[-2]
629
before = self.w32(self.loopC - self.loopL) < 0
631
self.loopC = self.w32(self.loopC + inc)
632
after = self.w32(self.loopC - self.loopL) < 0
634
finish = before > after
636
finish = before < after
641
self.leaves.append([])
642
self.code.append(self.dodo)
643
self.lit(len(self.code))
654
self.code.append(self.doloop)
656
leaves = self.leaves.pop()
658
self.code[p] = partial(self.code[p], len(self.code))
659
self.code.append(self.UNLOOP)
662
def question_do(self):
664
self.code.append(self.qdodo)
665
self.leaves.append([len(self.code)])
666
self.code.append(self.zbranch)
667
self.lit(len(self.code))
670
self.code.append(self.two_dup)
671
self.code.append(self.equal)
672
self.leaves.append([len(self.code)])
673
self.code.append(self.zbranch)
674
self.code.append(self.dodo)
675
self.lit(len(self.code))
684
self.loopL = self.r.pop()
685
self.loopC = self.r.pop()
693
self.leaves[-1].append(len(self.code))
694
self.code.append(self.branch)
697
self.q('SOURCE >R >R >IN @ >R')
698
self.q('SOURCEID @ >R -1 SOURCEID !')
699
self.q('SOURCEC ! SOURCEA ! 0 >IN !')
701
self.q('R> SOURCEID !')
702
self.q('R> >IN ! R> SOURCEA ! R> SOURCEC !')
712
r = self.ram[self.d[-2]] == c
724
if len(was) == 3 and was[0] == "'" and was[2] == "'":
726
self.lit(ord(was[1]))
731
if consume1(ord('$')):
733
elif consume1(ord('#')):
735
elif consume1(ord('%')):
739
neg = consume1(ord('-'))
741
double = consume1(ord('.'))
742
if self.d.pop() != 0:
760
def doubleAlso_comma():
762
if self.d.pop() == 2:
775
i += 3 * self.d.pop()
776
[ # nonimmediate number immediate
777
# ------------ ------ ---------
778
self.EXECUTE, doubleAlso, self.EXECUTE, # interpretation
779
self.compile_comma, doubleAlso_comma, self.EXECUTE # compilation
785
if self.d.pop() == 0:
790
self.q('TIB SOURCEA !')
796
def putcmd(self, cmd):
797
if cmd.endswith('\r'):
801
for i,c in enumerate(cmd):
802
self.ram[tib + i] = ord(c)
803
self.q('TIB SOURCEA !')
812
import Queue as queue
814
class AsyncSwapForth(SwapForth):
816
def __init__(self, cmdq, ready, *options):
817
SwapForth.__init__(self, *options)
823
assert 0, "REFILL failed"
824
self.lit(self.xt(self.interpret))
830
-4 : ": stack underflow",
831
-9 : ": invalid memory address",
832
-13 : ": undefined word",
833
-14 : ": interpreting a compile-only word",
834
-28 : ": user interrupt"}
835
self.out('error: %d%s\n' % (e, codes.get(e, "")))
840
(a, n) = self.popn(2)
842
(self.out, s) = self.cmdq.get()[:n]
844
self.ram[a:a + ns] = ba(s)
847
class Tethered(swapforth.TetheredTarget):
848
def __init__(self, *options):
849
self.searchpath = ['.']
850
self.log = open("log", "w")
853
self.interpreting = False
855
self.ready = threading.Event()
856
self.cmdq = queue.Queue()
857
self.t = threading.Thread(target = AsyncSwapForth, args = (self.cmdq, self.ready) + options)
858
self.t.setDaemon(True)
862
def issue(self, writer, cmd):
863
assert self.ready.is_set()
865
self.cmdq.put((writer, cmd))
868
def interactive_command(self, cmd):
869
self.issue(sys.stdout.write, cmd)
871
def command_response(self, cmd):
873
self.issue(lambda c: r.append(c), cmd)
876
if __name__ == '__main__':
882
options,args = getopt.getopt(sys.argv[1:], 'c:b')
883
optdict = dict(options)
885
cellsize = int(optdict['-c'])
888
except getopt.GetoptError:
890
print(" -c N cell size, one of 2,4 or 8")
891
print(" -b big-endian. Default is little-endian")
897
t = Tethered(cellsize, endian)
898
t.searchpath += ['../anstests', '../common']
899
# print set(t.sf.dict.keys()) - dpans['CORE']
902
t.include('swapforth.fs')
903
[t.include(a) for a in args]
904
except swapforth.Bye:
907
words = set(t.command_response('words').split())
908
missing = dpans['CORE'] - words
909
print(len(missing), "MISSING CORE", " ".join(sorted(missing)))