├── mnemo
├── version.py
├── lib
│ ├── math.mn
│ ├── vec.mn
│ └── sys.mn
├── mnemo.peg
└── __init__.py
├── examples
├── imp.mn
├── math.mn
├── die.mn
├── life.mn
├── vec.mn
├── while.mn
├── sp.mn
├── hello.mn
├── obj.mn
├── arr.mn
├── file.mn
├── mem.mn
├── mem2.mn
├── mem3.mn
├── array2d.mn
├── fib.mn
├── SafeBoolMRSW.mn
├── peterson.mn
├── pets.mn
├── Race.mn
├── snap.mn
├── SafeBoolMRSW2.mn
├── break.mn
├── RegBoolMRSW.mn
├── snap2.mn
├── testsuite.py
├── Stack.mn
└── RegIntMRSW.mn
├── setup.py
└── README.md
/mnemo/version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.0.9"
2 |
--------------------------------------------------------------------------------
/examples/imp.mn:
--------------------------------------------------------------------------------
1 | from "fib.mn" import fib
2 |
3 | fun main()
4 | println("answer=<",fib(4),">")
5 | end
6 |
--------------------------------------------------------------------------------
/examples/math.mn:
--------------------------------------------------------------------------------
1 | from "lib/math.mn" import sin, atan2
2 |
3 | fun main()
4 | print("sin=",sin(3))
5 | print("atan2=",atan2(1,1))
6 | end
7 |
--------------------------------------------------------------------------------
/examples/die.mn:
--------------------------------------------------------------------------------
1 | fun foo()
2 | assert(false)
3 | end
4 |
5 | fun bar()
6 | foo()
7 | end
8 |
9 | fun main()
10 | bar()
11 | end
12 |
--------------------------------------------------------------------------------
/examples/life.mn:
--------------------------------------------------------------------------------
1 | const life := (((80435758145817515**3) - (80538738812075974**3))
2 | + (12602123297335631**3))
3 |
4 | fun main()
5 | println("life=",life)
6 | end
7 |
--------------------------------------------------------------------------------
/examples/vec.mn:
--------------------------------------------------------------------------------
1 | from "lib/vec.mn" import vector, vector_add, vector_size
2 | fun main()
3 | atomic a := vector(10)
4 | a.add(3)
5 | println("a.size()=",a.size())
6 | end
7 |
--------------------------------------------------------------------------------
/examples/while.mn:
--------------------------------------------------------------------------------
1 | fun main()
2 | atomic i:=0
3 | while i < 10
4 | println("i=",i)
5 | i = i + 1
6 | end
7 | while (i > 0) and (i > 0)
8 | println("i=",i)
9 | i = i - 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/sp.mn:
--------------------------------------------------------------------------------
1 | atomic ar := new atomic[2,3,4]
2 |
3 | fun foo(li,a)
4 | println("li: ",li)
5 | println("type: ",type(li))
6 | println("len: ",len(li))
7 | println("a: ",a)
8 | end
9 |
10 | fun main()
11 | spawn(foo,ar,8)
12 | while NumThreads() > 1
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/examples/hello.mn:
--------------------------------------------------------------------------------
1 | # First program
2 |
3 | const a := 2
4 | const b := 3
5 | const ar := new atomic [1,2,3]
6 |
7 | fun main(args)
8 | println("Hello: ",a+2," ",3-b," ",1*4," ",1/4," ",ar)
9 | println(1.0*3.0, 1+2.2)
10 | atomic n := 0
11 | for i in 0 to 10
12 | n = n + i
13 | end
14 | println("n=",n)
15 | assert(n==55)
16 | end
17 |
--------------------------------------------------------------------------------
/examples/obj.mn:
--------------------------------------------------------------------------------
1 | fun printa(self)
2 | println("a=",self["a"])
3 | end
4 |
5 | atomic obj := new atomic {"a":3, "c":9, "b":1, "printa":printa }
6 |
7 | fun main()
8 | print(obj["a"],obj["b"],obj["c"])
9 | const x := obj.printa()
10 | obj.printa()
11 | atomic k := keys(obj)
12 | for i in 0 to len(k)-1
13 | println(i,": ",k[i])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/examples/arr.mn:
--------------------------------------------------------------------------------
1 | fun main()
2 | # Create an array of 3 atomics
3 | safe v := new atomic[9,2,3]
4 | assert(v[0] == 9)
5 | assert(v[1] == 2)
6 | assert(v[2] == 3)
7 | assert(len(v) == 3)
8 | # CAS = Compare and set
9 | if CAS(v,9,3)
10 | println("CAS!")
11 | else
12 | println("No CAS")
13 | end
14 | CAS(v,5,4)
15 | assert(v != 3)
16 | safe k := 2
17 | v[k] = 15
18 | assert(v[k] == 15)
19 | end
20 |
--------------------------------------------------------------------------------
/examples/file.mn:
--------------------------------------------------------------------------------
1 | from "lib/math.mn" import sin
2 | fun main()
3 | trace(false)
4 | const fd := open("/etc/group","r")
5 | atomic line := " "
6 | for i in 0 to 4 #while line != ""
7 | line = readline(fd)
8 | print("line: ",line)
9 | end
10 | const files := listdir(".")
11 | print("len(files)=",len(files))
12 | for i in 0 to min(5,len(files)-1)
13 | println(i,": ",files[i])
14 | end
15 | println(getcwd())
16 | println(sin(5))
17 | end
18 |
--------------------------------------------------------------------------------
/examples/mem.mn:
--------------------------------------------------------------------------------
1 | safe val := 0
2 | atomic msg := ""
3 |
4 | fun change_mem(n)
5 | atomic s := 0
6 | while true
7 | val = n
8 | s = val
9 | if ((s != 10) and (s != 11)) or (msg != "")
10 | if msg == ""
11 | msg = "bad value of val: %d" % s
12 | end
13 | return s
14 | end
15 | end
16 | end
17 |
18 | fun main()
19 | const th := spawn(change_mem,10)
20 | change_mem(11)
21 | join(th)
22 | println("msg: ",msg)
23 | end
24 |
--------------------------------------------------------------------------------
/examples/mem2.mn:
--------------------------------------------------------------------------------
1 | safe val := new safe[0]
2 | atomic msg := ""
3 |
4 | fun change_mem(n)
5 | atomic s := 0
6 | while true
7 | val[0] = n
8 | s = val[0]
9 | if ((s != 10) and (s != 11)) or (msg != "")
10 | if msg == ""
11 | msg = "bad value of val: %d" % s
12 | end
13 | return s
14 | end
15 | end
16 | end
17 |
18 | fun main()
19 | const th := spawn(change_mem,10)
20 | change_mem(11)
21 | join(th)
22 | println("msg: ",msg)
23 | end
24 |
--------------------------------------------------------------------------------
/examples/mem3.mn:
--------------------------------------------------------------------------------
1 | safe val := new safe { "val":0 }
2 | atomic msg := ""
3 |
4 | fun change_mem(n)
5 | atomic s := 0
6 | while true
7 | val["val"] = n
8 | s = val["val"]
9 | if ((s != 10) and (s != 11)) or (msg != "")
10 | if msg == ""
11 | msg = "bad value of val: %d" % s
12 | end
13 | return s
14 | end
15 | end
16 | end
17 |
18 | fun main()
19 | const th := spawn(change_mem,10)
20 | change_mem(11)
21 | join(th)
22 | println("msg: ",msg)
23 | end
24 |
--------------------------------------------------------------------------------
/mnemo/lib/math.mn:
--------------------------------------------------------------------------------
1 | fun sin(x)
2 | return pyexec("import math; retval = math.sin(args[1])",x)
3 | end
4 | fun cos(x)
5 | return pyexec("import math; retval = math.cos(args[1])",x)
6 | end
7 | fun tan(x)
8 | return pyexec("import math; retval = math.tan(args[1])",x)
9 | end
10 | fun sinh(x)
11 | return pyexec("import math; retval = math.sinh(args[1])",x)
12 | end
13 | fun cosh(x)
14 | return pyexec("import math; retval = math.cosh(args[1])",x)
15 | end
16 | fun tanh(x)
17 | return pyexec("import math; retval = math.tanh(args[1])",x)
18 | end
19 | fun atan2(x,y)
20 | return pyexec("import math; retval = math.atan2(args[1],args[2])",x,y)
21 | end
22 |
--------------------------------------------------------------------------------
/mnemo/lib/vec.mn:
--------------------------------------------------------------------------------
1 | fun vector_add(v,a)
2 | const m := v["len_"]
3 | if m >= v["size_"]
4 | atomic s2 := 2*v["size_"]
5 | atomic n := new atomic s2 of 0
6 | for i in 0 to v["size_"]-1
7 | n[i] = v["data_"][i]
8 | end
9 | v["data_"] = n
10 | v["size_"] = s2
11 | end
12 |
13 | atomic d := v["data_"]
14 | println("d: ",d)
15 | println("m: ",m)
16 | d[m] = a
17 | v["len_"] = m+1
18 | end
19 | fun vector_size(v)
20 | return v["len_"]
21 | end
22 | fun vector(size)
23 | atomic arr := new atomic {
24 | "size_" : size,
25 | "len_" : 0,
26 | "data_" : new atomic size of 0,
27 | "add" : vector_add,
28 | "size" : vector_size
29 | }
30 | return arr
31 | end
32 |
--------------------------------------------------------------------------------
/examples/array2d.mn:
--------------------------------------------------------------------------------
1 | fun main()
2 | trace(false)
3 | atomic a := new atomic 5 of (new atomic 8 of "$")
4 | for k in 0 to 4
5 | for m in 0 to 7
6 | a[k][m] = k + m
7 | end
8 | end
9 | atomic col := new atomic []
10 | for i in 0 to len(a)-1
11 | col = a[i]
12 | for j in 0 to len(col)-1
13 | print(col[j]," ")
14 | end
15 | println()
16 | end
17 | println()
18 | atomic i := 0
19 | atomic j := 0
20 | while i < len(a)
21 | j = 0
22 | col = a[i]
23 | while j < len(a[i])
24 | print(col[j]," ")
25 | j = j + 1
26 | end
27 | println()
28 | i = i + 1
29 | end
30 | println()
31 | println("len(a)=",len(a))
32 | end
33 |
--------------------------------------------------------------------------------
/examples/fib.mn:
--------------------------------------------------------------------------------
1 | fun foo()
2 | println("Fib!")
3 | end
4 |
5 | fun add(a,b)
6 | return a+b
7 | end
8 |
9 | fun fib(n)
10 | if n == 8
11 | brk()
12 | end
13 | if n < 2
14 | return n
15 | else
16 | return fib(n-1)+fib(n-2)
17 | end
18 | end
19 |
20 | fun figfib(n)
21 | const result := fib(n)
22 | println(">>fib(",n,")=",result)
23 | end
24 |
25 | fun main(args)
26 | atomic n1 := 3
27 | atomic n2 := 3
28 | if len(args) >= 1 n1 = int(args[0]) end
29 | if len(args) >= 2 n2 = int(args[1]) end
30 | println("n1=",n1," n2=",n2)
31 | const t1 := spawn(figfib,n1)
32 | const t2 := spawn(figfib,n2)
33 | assert(8 == fib(6))
34 | join(t1)
35 | join(t2)
36 | foo()
37 | const v := add(2,3)
38 | assert(v == 5)
39 | end
40 |
--------------------------------------------------------------------------------
/examples/SafeBoolMRSW.mn:
--------------------------------------------------------------------------------
1 | fun mkReg(n)
2 | return new safe n of false
3 | end
4 |
5 | const n := 3 # number of threads
6 |
7 | const reg := mkReg(n)
8 |
9 | fun test()
10 | const id := ThreadID()
11 | println("id: ",id)
12 | if id == (n-1)
13 | # I am the writer
14 | for j in 0 to n-1
15 | println("j=",j)
16 | reg[j] = true
17 | end
18 | else
19 | # I am a reader
20 | safe val := 0
21 | while true
22 | val = reg[id-1]
23 | println("id:",id," val: ",val)
24 | if val == true
25 | return 0
26 | end
27 | end
28 | end
29 | end
30 |
31 | fun main()
32 | trace(false)
33 | for i in 1 to n
34 | spawn(test)
35 | end
36 | while NumThreads() > 1
37 | end
38 | return 0
39 | end
40 |
--------------------------------------------------------------------------------
/examples/peterson.mn:
--------------------------------------------------------------------------------
1 | atomic mut := new atomic {
2 | "flag" : new atomic [0,0],
3 | "victim" : 0
4 | }
5 |
6 | fun lock()
7 | atomic me := ThreadID()
8 | assert((me == 0) or (me == 1))
9 | atomic other := 1 - ThreadID()
10 | mut["flag"][me] = 1
11 | mut["victim"] = me
12 | while (mut["flag"][other] == 1) and (mut["victim"] == me)
13 | end
14 | end
15 |
16 | fun unlock()
17 | atomic me := ThreadID()
18 | mut["flag"][me] = 1
19 | end
20 |
21 | atomic counter := 0
22 |
23 | fun incr()
24 | for i in 0 to 5
25 | lock()
26 | println(ThreadID()," lock success number ",i)
27 | for j in 0 to randint(0,9) end
28 | unlock()
29 | for j in 0 to randint(0,9) end
30 | end
31 | end
32 |
33 | fun main()
34 | lock()
35 | const t1 := spawn(incr)
36 | incr()
37 | join(t1)
38 | end
39 |
--------------------------------------------------------------------------------
/examples/pets.mn:
--------------------------------------------------------------------------------
1 | fun flag(mine, theirs, msg)
2 | atomic i := 0
3 | while i < 5
4 | mine[0] = true
5 | while theirs[0] == true
6 | end
7 | println(msg," ",i)
8 | i = i + 1
9 | mine[0] = false
10 | end
11 | end
12 |
13 | fun flag2(mine, theirs, msg)
14 | atomic i := 0
15 | while i < 5
16 | mine[0] = true
17 | while theirs[0] == true
18 | mine[0] = false
19 | end
20 | if mine[0] == true
21 | println(msg," ",i)
22 | mine[0] = false
23 | i = i + 1
24 | end
25 | end
26 | end
27 |
28 | fun main()
29 | atomic alice := new atomic[0]
30 | atomic bob := new atomic[0]
31 | const t1 := spawn(flag, alice, bob, "Alice's Pet Feeds")
32 | const t2 := spawn(flag, bob, alice, "Bob's Pet Feeds")
33 | join(t1)
34 | join(t2)
35 | end
36 |
--------------------------------------------------------------------------------
/examples/Race.mn:
--------------------------------------------------------------------------------
1 | fun lock(self)
2 | const ar := self["val"]
3 | while CAS(ar,0,1) == false
4 | end
5 | end
6 |
7 | fun unlock(self)
8 | const ar := self["val"]
9 | ar[0] = 0
10 | end
11 |
12 | fun mkMutex()
13 | return new atomic {
14 | "val":new atomic [0],
15 | "lock":lock,
16 | "unlock":unlock
17 | }
18 | end
19 |
20 | atomic val := 0
21 | atomic mutex := mkMutex()
22 | atomic nolock := false
23 |
24 | fun thread()
25 | for i in 0 to 49
26 | if nolock
27 | val = val + 1
28 | else
29 | mutex.lock()
30 | val = val + 1
31 | mutex.unlock()
32 | end
33 | end
34 | end
35 |
36 | fun main(args)
37 | const n := len(args)
38 | if n > 0
39 | if args[n-1] == "nolock"
40 | nolock = true
41 | end
42 | end
43 | for i in 0 to 10
44 | spawn(thread)
45 | end
46 | while NumThreads() > 1
47 | end
48 | println("val=",val)
49 | end
50 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | import re
3 |
4 | vfile="mnemo/version.py"
5 | verstrline = open(vfile, "rt").read()
6 | VSRE = r"^__version__\s*=\s*(['\"])(.*)\1"
7 | g = re.search(VSRE, verstrline, re.M)
8 | if g:
9 | __version__ = g.group(2)
10 | else:
11 | raise RuntimeError(f"Unable to find version in file '{vfile}")
12 |
13 | setup(
14 | name='mnemo',
15 | version=__version__,
16 | description='mnemo: The Mnemosyne Language interpreter',
17 | long_description='An implementation of a specialized parallel programming simulator in pure Python',
18 | url='https://github.com/stevenrbrandt/mnemo.git',
19 | author='Steven R. Brandt',
20 | author_email='steven@stevenrbrandt.com',
21 | license='LGPL',
22 | packages=['mnemo'],
23 | entry_points = {
24 | 'console_scripts' : ['mnemo=mnemo:main'],
25 | },
26 | package_data = {
27 | 'piraha': ['py.typed','mnemo.peg','lib'],
28 | },
29 | include_package_data=True,
30 | install_requires=['piraha']
31 | )
32 |
--------------------------------------------------------------------------------
/examples/snap.mn:
--------------------------------------------------------------------------------
1 | atomic values := new atomic [0,0,0]
2 |
3 | fun update(me, nvals, ovals)
4 | for i in 0 to 2
5 | ovals[i] = values[i]
6 | end
7 | atomic matched := true
8 | while true
9 | for i in 0 to 2
10 | nvals[i] = values[i]
11 | end
12 | matched = true
13 | for i in 0 to 2
14 | if nvals[i] != ovals[i]
15 | matched = false
16 | end
17 | end
18 | if matched
19 | assert(nvals[me] == values[me])
20 | values[me] = values[me] + 1
21 | println("thread: ",me," update: ",values[me])
22 | return 0
23 | else
24 | for i in 0 to 2
25 | ovals[i] = nvals[i]
26 | end
27 | println("thread: ",me," FAIL")
28 | end
29 | end
30 | end
31 |
32 | fun worker(me)
33 | atomic new_values := new atomic[0,0,0]
34 | atomic old_values := new atomic[0,0,0]
35 | while true
36 | update(me, new_values, old_values)
37 | end
38 | end
39 |
40 | fun main()
41 | trace(false)
42 | spawn(worker,0)
43 | spawn(worker,1)
44 | worker(2)
45 | end
46 |
--------------------------------------------------------------------------------
/examples/SafeBoolMRSW2.mn:
--------------------------------------------------------------------------------
1 | const n := 3 # number of threads
2 |
3 | fun safe_wr(self,val)
4 | const reg := self["values"]
5 | for i in 0 to n-1
6 | reg[i] = val
7 | end
8 | end
9 |
10 | fun safe_rd(self)
11 | const id := ThreadID()
12 | const reg := self["values"]
13 | return reg[id-1]
14 | end
15 |
16 | fun mkSafeBoolMRSW(n)
17 | return new safe {
18 | "values": new safe n of false,
19 | "write": safe_wr,
20 | "read": safe_rd
21 | }
22 | end
23 |
24 | const reg := mkSafeBoolMRSW(n)
25 |
26 | fun test()
27 | const id := ThreadID()
28 | println("id: ",id)
29 | if id == (n-1)
30 | # I am the writer
31 | println("writer: ",id)
32 | reg.write(true)
33 | else
34 | # I am a reader
35 | safe val := 0
36 | while true
37 | val = reg.read()
38 | println("id: ",id," val: ",val)
39 | if val == true
40 | return 0
41 | end
42 | end
43 | end
44 | end
45 |
46 | fun main()
47 | trace(false)
48 | for i in 1 to n
49 | spawn(test)
50 | end
51 | while NumThreads() > 1
52 | end
53 | return 0
54 | end
55 |
--------------------------------------------------------------------------------
/mnemo/lib/sys.mn:
--------------------------------------------------------------------------------
1 | fun exists(fn)
2 | return pyexec("import os; retval = os.path.exists(args[1])",fn)
3 | end
4 | fun chdir(d)
5 | return pyexec("import os; retval = os.chdir(args[1])",fn)
6 | end
7 | fun getcwd()
8 | return pyexec("import os; retval = os.getcwd()")
9 | end
10 | fun open(fn,rw)
11 | return pyexec("retval = open(args[1])",fn,rw)
12 | end
13 | fun readline(fn)
14 | return pyexec("retval = args[1].readline()",fn)
15 | end
16 | fun close(fn)
17 | return pyexec("retval = args[1].close()",fn)
18 | end
19 | fun listdir(d)
20 | return pyexec("import os; retval = os.listdir(args[1])",d)
21 | end
22 | fun keys(d)
23 | return pyexec("retval = [x for x in args[1].keys()]",d)
24 | end
25 | fun randint(a,b)
26 | return pyexec("import random; retval = random.randint(args[1],args[2])",a,b)
27 | end
28 | fun type(v)
29 | return pyexec("retval = type(args[1]).__name__",v)
30 | end
31 | fun int(v)
32 | return pyexec("retval = int(args[1])",v)
33 | end
34 | fun min(a,b)
35 | if a < b
36 | return a
37 | else
38 | return b
39 | end
40 | end
41 | fun max(a,b)
42 | if a > b
43 | return a
44 | else
45 | return b
46 | end
47 | end
48 | fun join(th)
49 | while true
50 | if is_alive(th) == false
51 | return 0
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/examples/break.mn:
--------------------------------------------------------------------------------
1 | fun main()
2 | atomic j := 0
3 | for i in 0 to 10
4 | println("i=",i)
5 | if i == 5
6 | j = i
7 | break
8 | end
9 | end
10 | assert(j == 5)
11 | j = 0
12 | for k in 0 to 10
13 | for i in 0 to 10
14 | println("i=",i)
15 | if i == 5
16 | j = i
17 | break
18 | end
19 | end
20 | if j != 0
21 | assert(j == 5)
22 | break
23 | end
24 | end
25 | assert(j == 5)
26 | j = 0
27 | while j < 10
28 | if j == 4
29 | break
30 | end
31 | j = j + 1
32 | end
33 | assert(j == 4)
34 | atomic k := 0
35 | atomic s := 0
36 | while k < 3
37 | j = 0
38 | while j < 10
39 | if j == 4
40 | s = j
41 | break
42 | end
43 | j = j + 1
44 | end
45 | k = k + 1
46 | if s > 0
47 | break
48 | end
49 | end
50 | assert(s == 4)
51 | j = 0
52 | k = 0
53 | for i in 0 to 10
54 | j = j + 1
55 | if i > 5
56 | continue
57 | end
58 | k = k + 1
59 | end
60 | assert(j == 11)
61 | assert(k == 6)
62 | println("Success ",j," ",k)
63 | end
64 |
--------------------------------------------------------------------------------
/mnemo/mnemo.peg:
--------------------------------------------------------------------------------
1 | skipper=([ \t\n\r]|\#.*)*\b
2 | keywords=(for|if|while|else|elif|end|fun|to|from|return|new|safe|regular|atomic|break|continue)
3 | name=(?!{-keywords}\b)[a-zA-Z_][a-zA-Z0-9_]*
4 | str = "(\\[\\\"]|[^\\"])*"
5 | cmp = (<=|>=|==|!=|<|>|and|or|xor)
6 | op = ({-cmp}|\*\*|[-+%*/])
7 | expr = {val}( {op} {val}|)
8 | num = -?[0-9]+
9 | exp = (e[+-]?[0-9]+)
10 | real = -?([0-9]+\.[0-9]*{-exp}?|\.[0-9]+{-exp}?|[0-9]+{-exp})
11 | memcall = {var} \. {name}
12 | fun = ({memcall}|{name}) \( {vals} \)
13 | array = \[ {-vals} \]
14 | alloc = new {qual} ({array}|{struct}|{sizer})
15 | sizer = {expr} of {expr}
16 | import = from {str} import {args}
17 | pair = {expr} : {expr}
18 | pairs = ({pair}( , {pair})*)?
19 | struct = \{ {pairs} \}
20 | elem = {name}( \[ {expr} \])+
21 | lambda = fun \( {args} \) {body} {end}
22 | var = ({elem}|{name})
23 | val = ({alloc}|{lambda}|{fun}|{var}|{real}|{num}|{str}|\( {expr} \))
24 | qual = (atomic|regular|safe|const)
25 | def = {qual} ({var}|{elem}) := {expr}
26 | assignOp = =
27 | assign=({elem}|{var}) {assignOp} {expr}
28 | for = for {name} in {expr} to {expr}
29 | while = while {expr}
30 | whilestmt = {while}( {-body})* {end}
31 | returnstmt = return {expr}
32 | if = if {expr}
33 | elif = elif {expr}
34 | else = else
35 | args = ({name}( , {name})*)?
36 | vals = ({expr}( , {expr})*)?
37 | start_fn = fun {name} \( {args} \)
38 | end = end
39 | ifstmt = {if} ({body} )*({elif} ({body} )*)?({else} ({body} )*)?{end}
40 | forstmt = {for} ({-body} )*{end}
41 | breakstmt = break
42 | continue = continue
43 | body = ({def}|{assign}|{fun}|{ifstmt}|{forstmt}|{whilestmt}|{returnstmt}|{breakstmt}|{continue})
44 | fun_def = {start_fn} ({-body} )*{end}
45 | random = {brk}
46 | prog = ^( {def}| {fun_def}| {import})* $
47 |
--------------------------------------------------------------------------------
/examples/RegBoolMRSW.mn:
--------------------------------------------------------------------------------
1 | const n := 3 # number of threads
2 |
3 | fun safe_wr(self,val)
4 | const reg := self["values"]
5 | for i in 0 to n-1
6 | reg[i] = val
7 | end
8 | end
9 |
10 | fun safe_rd(self)
11 | const id := ThreadID()
12 | const reg := self["values"]
13 | return reg[id-1]
14 | end
15 |
16 | fun mkSafeBoolMRSW(n)
17 | return new safe {
18 | "values": new safe n of false,
19 | "write": safe_wr,
20 | "read": safe_rd
21 | }
22 | end
23 |
24 | #########################################
25 |
26 | fun reg_wr(self,val)
27 | const old := self["old"]
28 | const safev := self["safe"]
29 | if old != val
30 | safev.write(val)
31 | self["old"] = val
32 | end
33 | end
34 |
35 | fun reg_rd(self)
36 | const safev := self["safe"]
37 | return safev.read()
38 | end
39 |
40 | fun mkRegBoolMRSW(n)
41 | return new safe {
42 | "safe" : mkSafeBoolMRSW(n),
43 | "old" : false,
44 | "write" : reg_wr,
45 | "read" : reg_rd
46 | }
47 | end
48 |
49 | #########################################
50 |
51 | const reg := mkRegBoolMRSW(n)
52 |
53 | fun test()
54 | const id := ThreadID()
55 | println("id: ",id)
56 | if id == (n-1)
57 | # I am the writer
58 | println("writer: ",id)
59 | reg.write(true)
60 | else
61 | # I am a reader
62 | safe val := 0
63 | while true
64 | val = reg.read()
65 | println("id: ",id," val: ",val)
66 | if val == true
67 | return 0
68 | end
69 | end
70 | end
71 | end
72 |
73 | fun main()
74 | trace(false)
75 | for i in 1 to n
76 | spawn(test)
77 | end
78 | while NumThreads() > 1
79 | end
80 | return 0
81 | end
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mnemosyne
2 | A programming language designed for CSC4585 http://csc.lsu.edu/csc4585/
3 |
4 |
5 |
6 | This language supports the constructs of Chapter 4 of "The Art of Multiprocessor Programming"
7 |
8 | * Safe memory - If a safe memory location is read while it is being written, a random number is obtained
9 | * Regular memory - If a regular memory location is read while it is being written, either the new or old value will be obtained at random.
10 | * Atomic memory - If an atomic memory location is read while it is being written, it will return either the new or old value, but once it returns the new value it will always do so.
11 |
12 | Mnemoysne is written using Python 3 and the Piraha parser.
13 |
14 | Mnemosyne supports...
15 |
16 | Function definitions with `fun` / `end`. The function
17 | named `main`, if present, is always executed.
18 | ```
19 | # functions and a special function named main
20 | fun main(args)
21 | println("Hello, world")
22 | end
23 | ```
24 |
25 | Declaration of varialbes with `:=`
26 |
27 | ```
28 | atomic a := 0 # an int
29 | safe s := "hello" # a string
30 | const pi := 3.14 # constant memory
31 | ```
32 |
33 | Allocation of arrays and structs with operator new...
34 |
35 | ```
36 | # a regular reference to an array of regluar
37 | # memory references
38 | regular r := new regular [0, 1, 3]
39 |
40 | # a two-dimensional memory declaration
41 | atomic a := new atomic 5 of (new atomic 8 of "$")
42 | ```
43 |
44 | Control flow including `for`, `while`, `if`, `elif` (for else if), and `else`.
45 | ```
46 | fun main()
47 | for i in 0 to 3
48 | println(i)
49 | end
50 | atomic j := 0
51 | while j < 10
52 | if j == 5
53 | break
54 | elif j < 5
55 | continue
56 | end
57 | end
58 | end
59 | ```
60 |
--------------------------------------------------------------------------------
/examples/snap2.mn:
--------------------------------------------------------------------------------
1 | atomic values := new atomic [0,0,0]
2 | atomic snaps := new atomic 3 of (new atomic 3 of 0)
3 |
4 | fun update(me, nvals, ovals, moved)
5 | for i in 0 to 2
6 | ovals[i] = values[i]
7 | end
8 | atomic matched := true
9 | atomic tmp := 0
10 | while true
11 | assert(type(nvals)=="list",type(nvals))
12 | for i in 0 to 2
13 | nvals[i] = values[i]
14 | moved[i] = false
15 | end
16 | matched = true
17 | for i in 0 to 2
18 | if nvals[i] != ovals[i]
19 | if moved[i]
20 | tmp = snaps[i]
21 | for j in 0 to 2
22 | nvals[j] = tmp[j]
23 | end
24 | matched = true
25 | println("thread: ",me," snap=",nvals[me])
26 | break
27 | else
28 | matched = false
29 | moved[i] = true
30 | end
31 | end
32 | end
33 | if matched
34 | snaps[me] = new atomic [nvals[0], nvals[1], nvals[2]]
35 | if nvals[me] != values[me]
36 | println("thread: ",me," n=",nvals[me]," v=",values[me])
37 | assert(false)
38 | end
39 | values[me] = values[me] + 1
40 | println("thread: ",me," update: ",values[me])
41 | return 0
42 | else
43 | for i in 0 to 2
44 | ovals[i] = nvals[i]
45 | end
46 | println("thread: ",me," FAIL")
47 | end
48 | end
49 | end
50 |
51 | fun worker(me)
52 | atomic new_values := new atomic[0,0,0]
53 | atomic old_values := new atomic[0,0,0]
54 | atomic snap_values := new atomic[0,0,0]
55 | atomic moved_values := new atomic[false,false,false]
56 | #while true
57 | for i in 0 to 1000
58 | update(me, new_values, old_values, moved_values)
59 | end
60 | end
61 |
62 | fun main()
63 | trace(false)
64 | spawn(worker,0)
65 | spawn(worker,1)
66 | worker(2)
67 | while NumThreads() > 1
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/examples/testsuite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os, re, sys
3 | import subprocess as s
4 | from termcolor import colored
5 |
6 | output = {
7 | "SafeBoolMRSW.mn":(["id:3 val: True"],["Assertion failure"]),
8 | "arr.mn":(["CAS!"],["Assertion failure"]),
9 | "array2d.mn":(["0 1 2 3 4 5","7 8 9 10 11","len(a)=5"],["Assertion failure"]),
10 | "break.mn":(["Success 11 6"],["Assertion failure"]),
11 | "die.mn":(["at file 'die.mn' line 3: end",
12 | "at file 'die.mn' line 6: foo()",
13 | "at file 'die.mn' line 10: bar()",
14 | "Assertion failure"],[]),
15 | "fib.mn":(["load: v -> 5"],["Assertion failure"]),
16 | "file.mn":(["line: root","5:","-0.95892"],["Assertion failure"]),
17 | "hello.mn":(["n=55"],["Assertion failure"]),
18 | "imp.mn":(["answer=<3>"],["Assertion failure"]),
19 | "life.mn":(["life=42"],["Assertion failure"]),
20 | "math.mn":(["atan2=0.785398"],["Assertion failure"]),
21 | "mem.mn":(["msg: bad value of val:"],["Assertion failure"]),
22 | "mem2.mn":(["msg: bad value of val:"],["Assertion failure"]),
23 | "mem3.mn":(["msg: bad value of val:"],["Assertion failure"]),
24 | "obj.mn":(["a=3"],["Assertion failure"]),
25 | "vec.mn":(["a.size()=1"],["Assertion failure"]),
26 | "while.mn":(["i=9"],["Assertion failure"]),
27 | "RegIntMRSW.mn":([],["Assertion failure"]),
28 | "Race.mn":(["val=550"],["Assertion failure"]),
29 | "Stack.mn":(["s=20"],["Assertion failure"]),
30 | "sp.mn":(["len: 3","a: 8"],["Assertion failure"]),
31 | }
32 |
33 | once = True
34 |
35 | for f in os.listdir("."):
36 | if re.match(r'.*\.mn$',f):
37 | if f not in output:
38 | print(colored("Skipping:","red"),f)
39 | continue
40 | print(colored("Running:","blue"),f)
41 | os.environ["PYTHONPATH"]=".."
42 | p = s.Popen([sys.executable,"-c","from mnemo import main, __file__; print('file:',__file__); main()","--bw",f], stdout=s.PIPE, stderr=s.PIPE, universal_newlines=True)
43 | o, e = p.communicate(0)
44 | if once:
45 | print(o.split('\n')[0])
46 | once = False
47 | print(colored(e,"red"),end='')
48 | has, hasnot = output[f]
49 | for h in has:
50 | if h not in o:
51 | raise Exception(h+" was not in output")
52 | for h in hasnot:
53 | if h in o:
54 | raise Exception(h+" was in output")
55 |
56 | print(colored("Success","green"))
57 |
--------------------------------------------------------------------------------
/examples/Stack.mn:
--------------------------------------------------------------------------------
1 | fun lock(self)
2 | const ar := self["val"]
3 | while CAS(ar,0,1) == false
4 | end
5 | end
6 |
7 | fun unlock(self)
8 | const ar := self["val"]
9 | ar[0] = 0
10 | end
11 |
12 | fun mkMutex()
13 | return new atomic {
14 | "val":new atomic [0],
15 | "lock":lock,
16 | "unlock":unlock
17 | }
18 | end
19 |
20 | fun condwait(self)
21 | const mutex := self["mutex"]
22 | const cno := self["condnum"]
23 | mutex.unlock()
24 | while cno == self["condnum"]
25 | end
26 | mutex.lock()
27 | end
28 |
29 | fun condnotify(self)
30 | const cno := self["condnum"]
31 | self["condnum"] = cno + 1
32 | end
33 |
34 | fun mkCond(m)
35 | return new atomic {
36 | "mutex":m,
37 | "condnum":0,
38 | "wait":condwait,
39 | "notify":condnotify
40 | }
41 | end
42 |
43 | fun push(self,v)
44 | const mutex := self["mutex"]
45 | mutex.lock()
46 | atomic pos := self["pos"]
47 | const val := self["values"]
48 |
49 | const empty := self["empty"]
50 | const full := self["full"]
51 |
52 | while pos == len(val)
53 | full.wait()
54 | pos = self["pos"]
55 | end
56 | assert((pos >= 0) and (pos < len(val)))
57 | val[pos] = v
58 | self["pos"] = pos + 1
59 | empty.notify()
60 | mutex.unlock()
61 | end
62 |
63 | fun pop(self)
64 | const mutex := self["mutex"]
65 | mutex.lock()
66 | atomic pos := self["pos"]-1
67 | const val := self["values"]
68 |
69 | const empty := self["empty"]
70 | const full := self["full"]
71 |
72 | while pos < 0
73 | empty.wait()
74 | pos = self["pos"]-1
75 | end
76 | assert((pos >= 0) and (pos < len(val)))
77 |
78 | const v := val[pos]
79 | self["pos"] = pos
80 | full.notify()
81 |
82 | mutex.unlock()
83 | return v
84 | end
85 |
86 | fun mkStack(cap)
87 | const m := mkMutex()
88 | return new atomic {
89 | "mutex":m,
90 | "full":mkCond(m),
91 | "empty":mkCond(m),
92 | "pos": 0,
93 | "values": new atomic cap of none,
94 | "push":push,
95 | "pop": pop
96 | }
97 | end
98 |
99 | const s := mkStack(2)
100 | const lim := 20
101 |
102 | fun pusher()
103 | for i in 0 to lim
104 | s.push(i)
105 | end
106 | end
107 |
108 | fun popper()
109 | for i in 0 to lim
110 | println("s=",s.pop())
111 | end
112 | end
113 |
114 | fun main()
115 | trace(false)
116 | println("START")
117 | spawn(popper)
118 | spawn(pusher)
119 | while NumThreads() > 1
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/examples/RegIntMRSW.mn:
--------------------------------------------------------------------------------
1 | const n := 3 # number of threads
2 |
3 | fun safe_wr(self,val)
4 | const reg := self["values"]
5 | for i in 0 to n-1
6 | reg[i] = val
7 | end
8 | end
9 |
10 | fun safe_rd(self)
11 | const id := ThreadID()
12 | const reg := self["values"]
13 | return reg[id-1]
14 | end
15 |
16 | fun mkSafeBoolMRSW(n)
17 | return new safe {
18 | "values": new safe n of false,
19 | "write": safe_wr,
20 | "read": safe_rd
21 | }
22 | end
23 |
24 | #########################################
25 |
26 | fun reg_wr(self,val)
27 | const old := self["old"]
28 | const safev := self["safe"]
29 | if old != val
30 | safev.write(val)
31 | self["old"] = val
32 | end
33 | end
34 |
35 | fun reg_rd(self)
36 | const safev := self["safe"]
37 | return safev.read()
38 | end
39 |
40 | fun mkRegBoolMRSW(n)
41 | return new safe {
42 | "safe" : mkSafeBoolMRSW(n),
43 | "old" : false,
44 | "write" : reg_wr,
45 | "read" : reg_rd
46 | }
47 | end
48 |
49 | #########################################
50 |
51 | const values := 8
52 |
53 | fun regi_wr(self,val)
54 | const reg := self["reg"]
55 | safe f := reg[val]
56 | f.write(true)
57 | for i in 0 to values
58 | if i != val
59 | f = reg[i]
60 | f.write(false)
61 | end
62 | end
63 | end
64 |
65 | fun regi_rd(self)
66 | const reg := self["reg"]
67 | safe f := 0
68 | for i in 0 to values
69 | f = reg[i]
70 | if f.read()
71 | return i
72 | end
73 | end
74 | return -1
75 | end
76 |
77 | fun mkRegIntMRSW(n)
78 | const a := new safe (values+1) of none
79 | for i in 0 to values
80 | a[i] = mkRegBoolMRSW(n)
81 | end
82 | return new safe {
83 | "reg" : a,
84 | "write" : regi_wr,
85 | "read" : regi_rd
86 | }
87 | end
88 |
89 | #########################################
90 |
91 | const reg := mkRegIntMRSW(n)
92 | const tstsize := 10
93 |
94 | fun test()
95 | const id := ThreadID()
96 | println("id: ",id)
97 | if id == (n-1)
98 | # I am the writer
99 | for i in 0 to tstsize
100 | if (i % 2) == 0
101 | reg.write(3)
102 | else
103 | reg.write(5)
104 | end
105 | end
106 | else
107 | # I am a reader
108 | safe val := 0
109 | for i in 0 to 2*tstsize
110 | val = reg.read()
111 | assert((val == 3) or (val == 5))
112 | end
113 | end
114 | end
115 |
116 | fun main()
117 | trace(false)
118 | for i in 1 to n
119 | spawn(test)
120 | end
121 | while NumThreads() > 1
122 | end
123 | return 0
124 | end
125 |
126 |
--------------------------------------------------------------------------------
/mnemo/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from typing import Dict, Any, List, Tuple, Union, Optional, cast
3 | from piraha import Grammar, compileFile, Matcher, Group
4 | from piraha.colored import colored
5 | from piraha.here import here
6 | import sys, os, re
7 | from random import randint, seed, random
8 | import argparse
9 | from .version import __version__
10 |
11 | def print_debug_help():
12 | print("""
13 | print var
14 | Print the value of variable var
15 |
16 | step threadno
17 | Tell thread threadno to execute one step
18 |
19 | pc
20 | Print the program counters for all threads
21 |
22 | stack
23 | Print a stack trace for all threads
24 |
25 | go or continue
26 | Resume normal execution""")
27 |
28 | parser = argparse.ArgumentParser(description='The Mnemosyne Interpreter')
29 | parser.add_argument('--seed', type=int, default=randint(0,1000000), help='set the random seed')
30 | parser.add_argument('--bw', action='store_true', default=False, help='disable text coloring')
31 | parser.add_argument('--no-trace', action='store_true', default=False, help='disable tracing')
32 | parser.add_argument('--debug', action='store_true', default=False, help='enable breakpoints')
33 | parser.add_argument('--parse', action='store_true', default=False, help='parse only')
34 | parser.add_argument('--inst', action='store_true', default=False, help='print instructions only')
35 | parser.add_argument('--opts', type=str, nargs="+", default=[], help='program args')
36 | parser.add_argument('--steps', type=int, default=1000000, help='program args')
37 | parser.add_argument('file', type=str, help='A Mnemosyne source file', nargs='+')
38 | pres=parser.parse_args(sys.argv[1:])
39 |
40 | def toint(n : Any)->int:
41 | if n is None:
42 | return 0
43 | assert type(n) == int
44 | return cast(int, n)
45 |
46 | DIV = '::'
47 | files : Dict[str,str] = {}
48 | logfd = open("log.txt","w")
49 | sval = pres.seed
50 | print("seed=%d" % sval,file=logfd)
51 | seed(sval)
52 |
53 | path_to_mnemo = os.path.dirname(os.path.realpath(__file__))
54 | mnemo_path = [path_to_mnemo, "."]
55 | if "MNEMO_PATH" in os.environ:
56 | mnemo_path += os.environ["MNEMO_PATH"].split(":")
57 |
58 | INDENT = " "
59 |
60 | #threads = []
61 |
62 | peg_file = os.path.join(path_to_mnemo, "mnemo.peg")
63 |
64 | g = Grammar()
65 | compileFile(g,os.path.join(mnemo_path[0],peg_file))
66 | with open(pres.file[0]) as fd:
67 | fc = fd.read()
68 | k : str = fc
69 | v : str = pres.file[0]
70 | files[k] = v
71 |
72 | def filename(group:Group)->str:
73 | return files[group.text]
74 |
75 | def loadstr(item : Any)->str:
76 | if type(item) == list:
77 | return "Array[%d]" % len(item)
78 | elif type(item) == dict:
79 | return "Struct[]"
80 | else:
81 | return str(item)
82 |
83 | class Func:
84 | def __init__(self,addr : int):
85 | self.addr = addr
86 | def __repr__(self)->str:
87 | return "Func(addr=%d)" % self.addr
88 |
89 | class BadAddress:
90 | def __repr__(self):
91 | return "BadAddress"
92 |
93 | class Var:
94 | def __init__(self,name : str,val : Any,qual : str):
95 | self.name = name
96 | self.val = val
97 | self.oldval = None
98 | self.qual = qual
99 | self.finished = True
100 | def show(self)->None:
101 | if self.finished:
102 | print(self.qual,": ",self.val,sep='')
103 | else:
104 | print(self.qual,": ",self.oldval," => ",self.val,sep='')
105 | def check(self):
106 | assert self.qual in ["atomic","const","regular","safe"]
107 | def set(self,v : Any)->None:
108 | self.finished = False
109 | self.oldval = self.val
110 | if self.val is None:
111 | self.val = v
112 | else:
113 | assert self.qual != "const", "Attempt to set a constant variable"
114 | self.val = v
115 | def get(self)->Any:
116 | if self.finished:
117 | return self.val
118 | elif self.qual == "atomic":
119 | if randint(1,3) == 1:
120 | self.oldval = self.val
121 | return self.oldval
122 | elif self.qual == "regular":
123 | if randint(1,2) == 1:
124 | return self.oldval
125 | else:
126 | return self.val
127 | elif self.qual == "safe":
128 | val : Any = None
129 | if type(self.val) == int:
130 | val = randint(-1000,1000)
131 | elif type(self.val) == float:
132 | val = 2000*(random()-.5)
133 | elif type(self.val) == bool:
134 | val = randint(0,1) == 0
135 | else:
136 | val = BadAddress()
137 | return val
138 | else:
139 | raise Exception(self.qual)
140 | def __repr__(self)->str:
141 | if type(self.val) in [str] or self.val is None:
142 | return "Var(%s,%s,'%s')" % (self.qual, type(self.val).__name__, self.val)
143 | elif type(self.val) in [int, float, Func, list] or self.val is None:
144 | return "Var(%s,%s,%s)" % (self.qual, type(self.val).__name__, self.val)
145 | else:
146 | return "Var(%s,%s)" % (self.qual, type(self.val).__name__ )
147 | def storeFinish(self)->None:
148 | self.finished = True
149 |
150 | def groupint(v : Union[Tuple[Group,int],Tuple[Group]])->Tuple[Group,int]:
151 | """
152 | This is a specialized casting function.
153 | Without iit, mypy will assert that v[1] is invalid.
154 | """
155 | assert len(v) == 2
156 | # Note that cast will trust us regardless of the type we specify
157 | # so we need to make sure it's actually allowed
158 | return cast(Tuple[Group,int], v)
159 |
160 | class Interp:
161 | def getvar(self,vname : str)->Var:
162 | if vname in self.vars[-1]:
163 | return self.vars[-1][vname]
164 | elif vname in self.vars[0]:
165 | return self.vars[0][vname]
166 | else:
167 | self.die("Invalid variable: '%s'" % vname)
168 | raise Exception() # Cannot get here
169 |
170 | def addr(self,f : Func)->int:
171 | self.massert(hasattr(f,"addr"),"Not a function object: "+str(f))
172 | return f.addr
173 |
174 | def done(self)->bool:
175 | return len(self.stack) == 0
176 |
177 | def __init__(self,gr : Group, runtime : 'Runtime', trace : bool)->None:
178 |
179 | self.runtime = runtime
180 | assert runtime is not None, "No runtime supplied to interpreter"
181 |
182 | self.id = self.runtime.thread_seq
183 | self.runtime.thread_seq += 1
184 | self.trace = trace
185 | self.gr = gr
186 | self.pc = -1
187 | self.stack : List[int] = []
188 | self.rets : List[Union[str,int]] = []
189 | self.vars : List[Dict[str,Var]] = [
190 | {"true":Var("true",True,"const"),
191 | "false":Var("false",False,"const"),
192 | "none":Var("none",None,"const"),
193 | "__version__":Var("__version__",__version__,"const"),
194 | "__python__":Var("__python__",sys.executable,"const"),
195 | "__mnemo__":Var("__mnemo__",__file__,"const"),
196 | }]
197 | self.loads : List[Dict[Union[int,str],Optional[Union[int,Var]]]] = [{}]
198 | self.inst : List[Union[Tuple[Group,int],Tuple[Group]]] = []
199 | self.indent = 0
200 | self.delay = 0
201 | for i in range(gr.groupCount()):
202 | self.load_instructions(gr.group(i))
203 |
204 | # Find functions
205 | for i in range(len(self.inst)):
206 | gm = self.inst[i][0]
207 | nm = gm.getPatternName()
208 | if nm == "start_fn":
209 | fnm = gm.group(0).substring()
210 | self.vars[0][fnm] = Var("fname",Func(i+1),"const")
211 |
212 | self.load_sys_functions(os.path.join(mnemo_path[0],os.path.join(path_to_mnemo,"lib","sys.mn")))
213 |
214 | def load_sys_functions(self,fname : str)->None:
215 | with open(fname,"r") as fd:
216 | fc = fd.read()
217 | files[fc] = fname
218 | m2 = Matcher(g,"prog",fc)
219 | if not m2.matches():
220 | m2.showError()
221 | for k in range(m2.gr.groupCount()):
222 | elem = m2.gr.group(k)
223 | nm = elem.getPatternName()
224 | if nm == "fun_def":
225 | func = elem.group(0).group(0).substring()
226 | ind = len(self.inst)+1
227 | self.vars[0][func] = Var("fun_def",Func(ind),"const")
228 | self.load_instructions(elem)
229 | if pres.inst:
230 | self.diag()
231 | exit(0)
232 |
233 | def diag(self)->None:
234 | # Diagnostic of the instruction set
235 | for i in range(len(self.inst)):
236 | print(colored(str(i)+":","yellow"),end=' ')
237 | nm = self.inst[i][0].getPatternName()
238 | if True: #nm in ["load", "store", "assign"]:
239 | print(colored(nm,"red"),self.inst[i][0].substring(),end=' ')
240 | else:
241 | print(colored(nm,"red"),end=' ')
242 | for j in range(1,len(self.inst[i])):
243 | print(colored(self.inst[i][j],"green"),end=' ')
244 | print()
245 | print('=====')
246 |
247 | def load_instructions(self,group:Group):
248 | nm = group.getPatternName()
249 | if nm in ["var", "fun"]:
250 | for i in range(group.groupCount()):
251 | self.load_instructions(group.group(i))
252 | load = Group("load",group.text,group.start,group.end)
253 | load.children += [group]
254 | self.inst += [(load,)]
255 | elif nm == "breakstmt" or nm == "continue":
256 | self.inst += [(group,)]
257 | elif nm == "ifstmt":
258 | prelist = []
259 | ilist = []
260 | glist = []
261 | for i in range(group.groupCount()):
262 | g = group.group(i)
263 | nm = g.getPatternName()
264 | if nm in ["else","elif"]:
265 | glist += [len(self.inst)]
266 | self.inst += [(Group("goto","",0,0),)]
267 | n1 = len(self.inst)
268 | self.load_instructions(group.group(i))
269 | if nm in ["if","elif","else","end"]:
270 | n2 = len(self.inst)-1
271 | prelist += [n1]
272 | ilist += [n2]
273 | for k in range(len(ilist)-1):
274 | k1 = ilist[k]
275 | k2 = prelist[k+1]
276 | self.inst[k1] = (self.inst[k1][0], k2)
277 | k1 = ilist[-1]
278 | k2 = ilist[0]
279 | self.inst[k1] = (self.inst[k1][0], k2)
280 | for g in glist:
281 | self.inst[g] = (self.inst[g][0], k1)
282 | elif nm == "assign" or nm == "def":
283 | self.load_instructions(group.group(2))
284 | self.load_instructions(group.group(0))
285 | self.inst += [(group,)]
286 | if nm == "assign":
287 | store = Group("store",group.text,group.start,group.end)
288 | store.children += [group.group(0)]
289 | self.inst += [(store,)]
290 | elif nm in ["expr", "val", "num", "op", "array", "str", "name", "args", "vals", "body", "real", "elem", "alloc", "qual", "struct", "pairs", "pair", "sizer", "memcall"]:
291 | for i in range(group.groupCount()):
292 | self.load_instructions(group.group(i))
293 | elif nm in ["start_fn", "end", "returnstmt", "for", "if", "elif", "else", "import", "while"]:
294 | for i in range(group.groupCount()):
295 | self.load_instructions(group.group(i))
296 | self.inst += [(group,)]
297 | elif nm in ["fun_def", "forstmt", "whilestmt"]:
298 | fstart = len(self.inst)
299 | for i in range(group.groupCount()):
300 | self.load_instructions(group.group(i))
301 | if i == 0:
302 | i0 = len(self.inst)-1
303 | endptr = len(self.inst)-1
304 | assert self.inst[endptr][0].getPatternName() == "end"
305 | if nm == "whilestmt":
306 | self.inst[i0] = (self.inst[i0][0], endptr) # point to end
307 | else:
308 | self.inst[fstart] = (self.inst[fstart][0], endptr) # point to end
309 | if nm == "forstmt":
310 | self.inst[endptr] = (self.inst[endptr][0], i0) # point to start
311 | else:
312 | self.inst[endptr] = (self.inst[endptr][0], fstart) # point to start
313 | else:
314 | raise Exception(nm)
315 | def getval(self,expr : Group)->Any:
316 | nm = expr.getPatternName()
317 | if nm == "expr":
318 | if expr.groupCount() == 1:
319 | return self.getval(expr.group(0))
320 | elif expr.groupCount() == 3:
321 | val1 = self.getval(expr.group(0))
322 | t1 = type(val1)
323 | op = expr.group(1).substring()
324 | val2 = self.getval(expr.group(2))
325 | t2 = type(val2)
326 | if t1 == int and t2 == float:
327 | val1 = float(val1)
328 | t1 = float
329 | elif t1 == float and t2 == int:
330 | val2 = float(val2)
331 | t2 = float
332 | if op == "+":
333 | return val1+val2
334 | elif op == "-":
335 | return val1-val2
336 | elif op == "*":
337 | return val1*val2
338 | elif op == "/":
339 | if [t1,t2]==[int,int]:
340 | return val1//val2
341 | else:
342 | return val1/val2
343 | elif op == "==":
344 | return val1==val2
345 | elif op == "<":
346 | return val1":
348 | return val1>val2
349 | elif op == "<=":
350 | return val1<=val2
351 | elif op == ">=":
352 | return val1>=val2
353 | elif op == "!=":
354 | return val1!=val2
355 | elif op == "and":
356 | return val1 and val2
357 | elif op == "or":
358 | return val1 or val2
359 | elif op == "%":
360 | return val1 % val2
361 | elif op == "**":
362 | return val1 ** val2
363 | raise Exception("op="+op)
364 | elif nm == "val":
365 | return self.getval(expr.group(0))
366 | elif nm == "num":
367 | return int(expr.substring())
368 | elif nm == "str":
369 | sval = expr.substring()
370 | return sval[1:-1]
371 | elif nm == "var" or nm == "fun":
372 | if expr.start not in self.loads[-1]:
373 | print("loads:",self.id,self.loads)
374 | return self.loads[-1][expr.start]
375 | elif nm == "real":
376 | return float(expr.substring())
377 | elif nm == "alloc":
378 | qual = expr.group(0).substring()
379 | vals = expr.group(1)
380 | vnm = expr.group(1).getPatternName()
381 | if vnm == "array":
382 | ar = []
383 | for i in range(vals.groupCount()):
384 | ar += [Var("&",self.getval(vals.group(i)),qual)]
385 | elif vnm == "struct":
386 | d = {}
387 | hash = vals.group(0)
388 | for i in range(hash.groupCount()):
389 | pair = self.getval(hash.group(i))
390 | pair[1].qual = qual
391 | d[pair[0]] = pair[1]
392 | return d
393 | elif vnm == "sizer":
394 | ar = []
395 | count = self.getval(vals.group(0))
396 | for i in range(count):
397 | item = self.getval(vals.group(1))
398 | ar += [Var("&",item,qual)]
399 | return ar
400 | else:
401 | raise Exception(vnm)
402 | return ar
403 | elif nm == "pair":
404 | v0 : str = self.getval(expr.group(0))
405 | v1 : Var = self.getval(expr.group(1))
406 | var1 = Var('&',v1,"const")
407 | return (v0, var1)
408 | elif nm == "memcall":
409 | dvar = self.getval(expr.group(0))
410 | return dvar[expr.group(1).substring()]
411 | elif nm == "pairs" or nm == "vals":
412 | ar = []
413 | for k in range(expr.groupCount()):
414 | ar += self.getval(expr.group(k))
415 | return ar
416 |
417 | print("expr:",expr.dump())
418 | print("line:",expr.linenum())
419 | raise Exception(nm)
420 | def massert(self,test : bool,msg : str)->None:
421 | if not test:
422 | self.die(msg)
423 | def stack_trace(self)->None:
424 | print("Stack for thread:",self.id)
425 | self.stack += [self.pc] # Add current location to the stack
426 | for k in range(len(self.stack)-1,-1,-1):
427 | pc = self.stack[k]
428 | if pc < len(self.inst):
429 | pg = self.inst[pc][0]
430 | print(" at file '%s' line %d: %s" % (filename(pg), pg.linenum(), pg.substring()))
431 | self.stack = self.stack[:-1]
432 | def die(self,msg : str)->None:
433 | print("Die:",msg)
434 | self.stack += [self.pc] # Add current location to the stack
435 | for k in range(len(self.stack)-1,-1,-1):
436 | pc = self.stack[k]
437 | if pc < len(self.inst):
438 | pg = self.inst[pc][0]
439 | print(" at file '%s' line %d: %s" % (filename(pg), pg.linenum(), pg.substring()))
440 | #raise Exception("die")
441 | exit(0)
442 |
443 | def start_call(self,expr : Group,retkey : Union[str,int])->None:
444 | vals = expr.group(1)
445 | args = []
446 | for k in range(vals.groupCount()):
447 | args += [ self.getval(vals.group(k)) ]
448 | self.start_call2(expr,retkey,args)
449 |
450 | def start_call2(self,expr : Group,retkey : Union[int,str],args : List[str])->None:
451 | ####
452 | fnm = expr.group(0).substring()
453 | if fnm == "print" or fnm == "println":
454 | for arg in args:
455 | print(arg,end='')
456 | if fnm == "println":
457 | print()
458 | self.pc += 1
459 | return
460 | elif fnm == "pyexec":
461 | self.pc += 1
462 | retval : Any = None
463 | eglobs = {"retval":None,"args":args,"Var":Var}
464 | exec(args[0],{},eglobs)
465 | retval = eglobs["retval"]
466 | if type(retval) == list:
467 | retval = [Var('&',di,'const') for di in retval]
468 | self.loads[-1][retkey] = retval
469 | return
470 | elif fnm == "trace":
471 | val = args[0]
472 | if val == True:
473 | self.trace = True
474 | elif val == False:
475 | self.trace = False
476 | else:
477 | raise Exception(val)
478 | self.pc += 1
479 | return
480 | elif fnm == "assert":
481 | self.pc += 1
482 | val = args[0]
483 | if val != True:
484 | self.die("Assertion failure: "+expr.substring())
485 | return
486 | elif fnm == "spawn":
487 | vals = expr.group(1)
488 | newthread = Interp(self.gr, self.runtime, self.trace)
489 | newthread.trace = self.runtime.debug
490 | newthread.indent = 0
491 | #for k in self.vars[0].keys():
492 | # newthread.vars[0][k] = self.vars[0][k]
493 | newthread.vars[0] = self.vars[0]
494 | newthread.pc = len(self.inst)
495 |
496 | # Prepare spawn return location
497 | if vals.groupCount()>1:
498 | newthread.loads[-1][vals.group(1).start] = newthread.id # vid
499 |
500 | newthread.start_call2(vals,expr.start,args[1:])
501 | self.runtime.threads += [newthread]
502 | self.pc += 1
503 | vid = Var("id",newthread.id,"const")
504 | self.loads[-1][retkey] = newthread.id # vid
505 | return
506 | elif fnm == "is_alive":
507 | self.pc += 1
508 | pid = args[0]
509 | found : bool = False
510 | for th in self.runtime.threads:
511 | if pid == th.id:
512 | found = True
513 | break
514 | vb = found # Var("alive",found,"const")
515 | self.loads[-1][retkey] = vb
516 | return
517 | elif fnm == "brk":
518 | self.pc += 1
519 | self.runtime.stepmode = True
520 | return
521 | elif fnm == "len":
522 | self.pc += 1
523 | vals = self.getval(expr.group(1))
524 | self.loads[-1][retkey] = len(vals)
525 | return
526 | elif fnm == "ThreadID":
527 | self.pc += 1
528 | self.loads[-1][retkey] = self.id
529 | return
530 | elif fnm == "NumThreads":
531 | self.pc += 1
532 | self.loads[-1][retkey] = len(self.runtime.threads)
533 | return
534 | elif fnm == "CAS":
535 | self.pc += 1
536 | vname = expr.group(1).group(0).substring()
537 | if vname in self.vars[-1]:
538 | var = self.vars[-1][vname]
539 | elif vname in self.vars[0]:
540 | var = self.vars[0][vname]
541 | else:
542 | self.die("CAS: No such variable: "+vname)
543 | self.massert(type(var.val) == list,"Arg 0 of CAS must be a list")
544 | self.massert(len(var.val) > 0,"Array arg of CAS must have length of 1 or more")
545 | self.massert(var.val[0].qual == "atomic","Arg 0 of CAS must be atomic")
546 | oldval = args[1]
547 | newval = args[2]
548 | if var.val[0].val == oldval:
549 | var.val[0].val = newval
550 | var.val[0].storeFinish()
551 | retval = True
552 | else:
553 | retval = False
554 | self.loads[-1][retkey] = retval
555 | return
556 | ####
557 | mfnm = expr.group(0).getPatternName()
558 | add_self = 0
559 | # If we come here through spawn, we'll see expr
560 | if mfnm == "expr" or mfnm == "name":
561 | fname = expr.group(0).substring()
562 | try:
563 | funinst = self.addr(self.vars[0][fname].get())
564 | except KeyError as ke:
565 | self.die(str(ke))
566 | elif mfnm == "memcall":
567 | add_self = 1
568 | fname = expr.group(0).substring()
569 | vname = expr.group(0).group(0).substring()
570 | funinst = self.addr(self.getval(expr.group(0)).get())
571 | else:
572 | raise Exception(mfnm)
573 | if self.trace:
574 | print(INDENT * self.indent,colored(str(self.id)+": ","blue"),colored("start call: ","green"),colored(fname,"blue"),sep='')
575 | print(str(self.id),DIV,"start call: ",fname,sep='',file=logfd)
576 | self.indent += 1
577 | funval = self.inst[funinst-1][0]
578 | argdefs = funval.group(1)
579 | if expr.groupCount() > 1:
580 | argvals = expr.group(1)
581 | else:
582 | # Handle no args
583 | argvals = Group("","",0,0)
584 | n1 = argdefs.groupCount()
585 | #n2 = argvals.groupCount()+add_self
586 | n2 = len(args)+add_self
587 | self.massert(n1 == n2,"Arity mismatch for '"+fname+"()' %d != %d" % (n1,n2))
588 | self.vars += [{}]
589 | for i in range(argdefs.groupCount()):
590 | argname = argdefs.group(i).substring()
591 | if add_self == 1 and i == 0:
592 | argval = self.getval(expr.group(0).group(0))
593 | else:
594 | argval = args[i-add_self] #self.getval(argvals.group(i-add_self))
595 | argvar = Var(argname, argval, "const")
596 | self.vars[-1][argname] = argvar
597 | self.loads += [{}]
598 | self.stack += [self.pc]
599 | self.rets += [retkey]
600 | self.pc = funinst
601 | return
602 |
603 | def end_call(self,retval : Optional[Var])->None:
604 | self.indent -= 1
605 | self.pc = self.stack[-1]+1
606 | retkey = self.rets[-1]
607 |
608 | self.stack = self.stack[:-1]
609 | self.vars = self.vars[:-1]
610 | self.loads = self.loads[:-1]
611 | self.rets = self.rets[:-1]
612 |
613 | self.loads[-1][retkey] = retval
614 | if self.trace:
615 | print(INDENT * self.indent,colored(str(self.id)+": ","blue"),colored("end call->%s" % (loadstr(retval)),"green"),sep='')
616 | print(str(self.id),DIV,"end call->%s" % (loadstr(retval)),sep='',file=logfd)
617 |
618 | def screen_log(self, fn : str, linenum : int, nm : str, msg : str, logfd):
619 | pre = f"{fn}:{linenum},"
620 | if self.trace:
621 | print(INDENT*self.indent,colored(str(self.id)+": ","blue"),colored(pre, "green")," ",colored("load: "+msg,"blue"),sep='',end=' ')
622 | print(str(self.id),DIV,pre," ","load: "+msg,sep='',end=' ',file=logfd)
623 |
624 | def step(self,feedback : List[bool])->bool:
625 | assert self.pc >= 0,str(self.pc)
626 | feedback[0] = False
627 | if self.pc >= len(self.inst):
628 | return False
629 | if self.delay > 0:
630 | if self.trace:
631 | print(INDENT*self.indent,colored("%d: " % self.id,"blue"),colored("delay","green"),sep='')
632 | print("%d:: " % self.id,"delay",sep='',file=logfd)
633 | self.delay -= 1
634 | return True
635 | s = self.inst[self.pc][0]
636 | fn = os.path.basename(files.get(s.text,"?"))
637 | nm = s.getPatternName()
638 | self.screen_log(fn, s.linenum(), nm, s.substring(), logfd)
639 | pre = f"{fn}:{s.linenum()},"
640 | if True:
641 | pass # yyy
642 | elif nm == "load":
643 | if self.trace:
644 | print(INDENT*self.indent,colored(str(self.id)+": ","blue"),colored(pre, "green")," ",colored("load: "+s.substring(),"blue"),sep='',end=' ')
645 | print(str(self.id),DIV,pre," ","load: "+s.substring(),sep='',end=' ',file=logfd)
646 | elif nm == "start_fn":
647 | pass #print(colored("step: "+str(self.pc),"green"),colored("define function: "+s.substring(),"blue"))
648 | feedback[0] = True
649 | elif nm == "store":
650 | if self.trace:
651 | print(INDENT*self.indent,colored(str(self.id)+": ","blue"),colored(pre, "green")," ",colored("finish: "+s.substring(),"blue"),sep='')
652 | print(str(self.id)+":: ", pre," ","finish: "+s.substring(),sep='',file=logfd)
653 | elif nm == "assign":
654 | if self.trace:
655 | print(INDENT*self.indent,colored(str(self.id)+": ","blue"),colored(pre, "green")," ",colored("start: "+s.substring(),"blue"),sep='')
656 | print(str(self.id),DIV, pre," ","start: "+s.substring(),sep='',file=logfd)
657 | else:
658 | if self.trace:
659 | print(INDENT*self.indent,colored(str(self.id)+": ","blue"),colored(pre, "green")," ",colored(s.substring(),"blue"),sep='')
660 | print(str(self.id),DIV, pre," ",s.substring(),sep='',file=logfd)
661 | if nm == "start_fn":
662 | vv = groupint(self.inst[self.pc])
663 | self.pc = vv[1]+1
664 | return True
665 | elif nm == "load":
666 | if s.group(0).getPatternName() == "var":
667 | vg = s.group(0)
668 | elems : List[Group] = []
669 | if vg.group(0).getPatternName() == "name":
670 | vname = vg.substring()
671 | else:
672 | elg = vg.group(0)
673 | vname = elg.group(0).substring()
674 | if elg.group(1).getPatternName() == "expr":
675 | elems = [elg.group(1)]
676 | else:
677 | elems = elg.group(1).children
678 | var : Var
679 | if vname in self.vars[-1]:
680 | var = self.vars[-1][vname]
681 | elif vname in self.vars[0]:
682 | var = self.vars[0][vname]
683 | else:
684 | self.die('No variable named: '+vname)
685 | for ch in elems:
686 | chv = self.getval(ch)
687 | try:
688 | var = var.get()[chv]
689 | except TypeError as te:
690 | self.die("Not subscriptable: "+str(chv))
691 | except IndexError as ie:
692 | self.die("Index error: "+str(chv))
693 | except KeyError as ke:
694 | self.die("Key Error: "+str(chv))
695 | val = var.get()
696 | self.loads[-1][s.group(0).start] = val
697 | if self.trace:
698 | print(colored("-> "+loadstr(val),"yellow"))
699 | print("-> "+loadstr(val),file=logfd)
700 | self.pc += 1
701 | return True
702 | elif s.group(0).getPatternName() == "fun":
703 | expr = s.group(0)
704 | if self.trace:
705 | print()
706 | print(file=logfd)
707 | self.start_call(expr,s.start)
708 | return True
709 | else:
710 | raise Exception()
711 | elif nm == "def":
712 | qual = s.group(0).substring()
713 | val = self.getval(s.group(2))
714 | if s.group(1).getPatternName() == "var":
715 | vname = s.group(1).substring()
716 | self.massert(vname not in self.vars[-1],"Redefinition of "+vname+" at line "+str(s.linenum()))
717 | var = Var(vname,val,qual)
718 | self.vars[-1][vname] = var
719 | self.pc += 1
720 | return True
721 | elif nm == "assign":
722 | op = s.group(1).substring()
723 | val = self.getval(s.group(2))
724 | self.delay = randint(0,2)
725 | nm = s.group(0).getPatternName()
726 | if nm == "var":
727 | vname = s.group(0).substring()
728 | if op == ":=":
729 | self.massert(vname not in self.vars[-1],"Redefinition of "+vname+" at line "+str(s.linenum()))
730 | var = Var("&",vname,val)
731 | self.vars[-1][vname] = var
732 | self.pc += 1
733 | return True
734 | elif op == "=":
735 | self.massert((vname in self.vars[-1]) or (vname in self.vars[0]),"Undefined variable '"+vname+"' at line "+str(s.linenum()))
736 | if vname in self.vars[-1]:
737 | var = self.vars[-1][vname]
738 | else:
739 | var = self.vars[0][vname]
740 | var.set(val)
741 | self.store_var = var
742 | self.pc += 1
743 | return True
744 | else:
745 | raise Exception(op)
746 | elif nm == "elem":
747 | self.pc += 1
748 | lhs = s.group(0)
749 | vname = lhs.group(0).substring()
750 | if vname in self.vars[-1]:
751 | var = self.vars[-1][vname]
752 | else:
753 | var = self.vars[0][vname]
754 | for i in range(1,lhs.groupCount()):
755 | ind = self.getval(lhs.group(i))
756 | try:
757 | var = var.get()[ind]
758 | except IndexError as ie:
759 | self.die(str(ie))
760 | rhs = self.getval(s.group(2))
761 | op = s.group(1).substring()
762 | if op == "=":
763 | self.store_var = var
764 | var.set(rhs)
765 | return True
766 | raise Exception()
767 | else:
768 | raise Exception(nm)
769 | elif nm == "end":
770 | if len(self.stack) == 0:
771 | return False
772 | step_info = self.inst[self.pc]
773 | assert len(step_info) == 2
774 | endix = groupint(step_info)[1]
775 | start_info = self.inst[endix]
776 | ends = start_info[0].getPatternName()
777 | if ends == "start_fn":
778 | self.end_call(None)
779 | elif ends == "if":
780 | self.pc += 1
781 | elif ends == "for":
782 | loopvar = start_info[0].group(0).substring()
783 | startval = self.getval(start_info[0].group(1))
784 | endval = self.getval(start_info[0].group(2))
785 | oldval = self.vars[-1][loopvar].get()
786 | if oldval < endval:
787 | self.vars[-1][loopvar] = Var(loopvar,oldval+1,"const")
788 | self.pc = endix
789 | else:
790 | del self.vars[-1][loopvar]
791 | self.pc += 1
792 | elif ends == "load":
793 | # This is a while statement
794 | self.pc = groupint(step_info)[1]
795 | else:
796 | raise Exception(ends)
797 | return True
798 | elif nm == "returnstmt":
799 | retval = self.getval(s.group(0))
800 | self.end_call(retval)
801 | return True
802 | elif nm == "for":
803 | loopvar = s.group(0).substring()
804 | startval = self.getval(s.group(1))
805 | endval = self.getval(s.group(2))
806 | self.massert(loopvar not in self.vars[-1],"Loop attempts to redefine "+loopvar)
807 | self.vars[-1][loopvar] = Var(loopvar,startval,"const")
808 | self.pc += 1
809 | return True
810 | elif nm == "if" or nm == "elif":
811 | bval = self.getval(s.group(0))
812 | self.massert(type(bval) == bool, "If expression does not evaluate to boolean "+s.substring())
813 | if bval:
814 | self.pc += 1
815 | return True
816 | else:
817 | self.pc = groupint(self.inst[self.pc])[1]
818 | return True
819 | elif nm == "else":
820 | self.pc += 1
821 | return True
822 | elif nm == "goto":
823 | self.pc = groupint(self.inst[self.pc])[1]
824 | return True
825 | elif nm == "import":
826 | fname = self.getval(s.group(0))
827 | vals = s.group(1)
828 | funs = []
829 | for i in range(vals.groupCount()):
830 | funs += [vals.group(i).substring()]
831 |
832 | # Locate path
833 | for path in mnemo_path:
834 | ftemp = os.path.join(path, fname)
835 | if os.path.exists(ftemp):
836 | fname = ftemp
837 | break
838 |
839 | with open(fname,"r") as fd:
840 | fc = fd.read()
841 |
842 | # Import
843 | files[fc] = fname
844 | m2 = Matcher(g,"prog",fc)
845 | if not m2.matches():
846 | m2.showError()
847 | for k in range(m2.gr.groupCount()):
848 | elem = m2.gr.group(k)
849 | nm = elem.getPatternName()
850 | if nm == "fun_def":
851 | func = elem.group(0).group(0).substring()
852 | if func in funs:
853 | ind = len(self.inst)+1
854 | self.vars[0][func] = Var("fun_def",Func(ind),"const")
855 | self.load_instructions(elem)
856 | #print("fdef:",func,"->",ind,self.inst[ind][0].dump())
857 | self.pc += 1
858 | return True
859 | elif nm == "store":
860 | self.store_var.storeFinish()
861 | self.pc += 1
862 | return True
863 | elif nm == "while":
864 | val = self.getval(s.group(0))
865 | if val == True:
866 | self.pc += 1
867 | elif val == False:
868 | self.pc = groupint(self.inst[self.pc])[1] + 1
869 | else:
870 | raise Exception(val)
871 | return True
872 | elif nm == "breakstmt" or nm == "continue":
873 | while self.pc < len(self.inst):
874 | step_info = self.inst[self.pc]
875 | if step_info[0].getPatternName() == "end":
876 | endix = groupint(step_info)[1]
877 | start_info = self.inst[endix]
878 | if start_info[0].getPatternName() == "for":
879 | #ends = start_info[0].getPatternName()
880 | if nm == "breakstmt":
881 | loopvar = start_info[0].group(0).substring()
882 | del self.vars[-1][loopvar]
883 | self.pc += 1
884 | return True
885 | elif start_info[0].getPatternName() in ["load", "while"]:
886 | if nm == "breakstmt":
887 | self.pc += 1
888 | return True
889 | self.pc += 1
890 | return True
891 | raise Exception(s.dump())
892 | return False
893 | def dump(self):
894 | print(colored("thread id:","cyan"),self.id)
895 | for fno in range(len(self.vars)):
896 | print(colored(" frame:","green"),fno)
897 | f = self.vars[fno]
898 | for var in f:
899 | print(colored(" var:","yellow"),var,"->",f[var])
900 |
901 | class Runtime:
902 | def dump(self):
903 | for thread in self.threads:
904 | thread.dump()
905 |
906 | def __init__(self, src_file, trace : bool=True, debug : bool=False)->None:
907 | self.stepmode = False
908 | self.trace = trace
909 | self.debug = debug
910 | self.threads : List[Interp] = []
911 | self.thread_seq = 0
912 | self.stop = False
913 |
914 | g = Grammar()
915 | compileFile(g,os.path.join(mnemo_path[0],peg_file))
916 | with open(src_file) as fd:
917 | fc = fd.read()
918 | k : str = fc
919 | v : str = src_file
920 | files[k] = v
921 | m = Matcher(g,"prog",fc)
922 | if m.matches():
923 | self.interp = Interp(m.gr, self, self.trace)
924 | self.threads = [self.interp]
925 | self.interp.pc = 0
926 | else:
927 | m.showError()
928 | raise Exception()
929 |
930 | def run_step(self):
931 | if self.stop:
932 | print(colored("Breakpoint","red"))
933 | return False
934 | while True:
935 | lo = 0
936 | hi = len(self.threads)-1
937 | if hi < 0:
938 | return False
939 | if lo == hi:
940 | tno = lo
941 | else:
942 | tno = randint(lo, hi)
943 |
944 | # The debugger
945 | stack_wait = 0
946 | if self.debug and self.stepmode:
947 | while True:
948 | print('$ ',end='')
949 | sys.stdout.flush()
950 |
951 | cmd = sys.stdin.readline().strip()
952 |
953 | if cmd == "":
954 | self.stepmode = False
955 | break
956 |
957 | rm = re.match(r'\s*stack\b',cmd)
958 | if rm:
959 | for th in self.threads:
960 | th.stack_trace()
961 | continue
962 |
963 | rm = re.match(r'\s*(continue|cont|go)\b',cmd)
964 | if rm:
965 | self.stepmode = False
966 | break
967 |
968 | rm = re.match(r'\s*print\s*(\w+)',cmd)
969 | if rm:
970 | vname = rm.group(1)
971 | for th in self.threads:
972 | if vname in th.vars[0]:
973 | print("thread:",th.id,"global:",vname,"=",end=' ')
974 | th.vars[0][vname].show()
975 | elif vname in th.vars[-1]:
976 | print("thread:",th.id,"local:",vname,"=",end=' ')
977 | th.vars[-1][vname].show()
978 | continue
979 |
980 | rm = re.match(r'\s*pc\b',cmd)
981 | if rm:
982 | for th in self.threads:
983 | ex_item = th.inst[th.pc][0]
984 | print("thread:",th.id,"line:",ex_item.linenum(),"->",ex_item.substring())
985 | continue
986 |
987 | rm = re.match(r'\s*step\s+(\d+)',cmd)
988 | if rm:
989 | tnum = int(rm.group(1))
990 | tinp = None
991 | for k in range(len(self.threads)):
992 | if self.threads[k].id == tnum:
993 | tinp = k
994 | if tinp is None:
995 | print(colored("No such thread: "+rm.group(1),"red"),end=' threads: ')
996 | for t in self.threads:
997 | print(t.id,end=' ')
998 | print()
999 | else:
1000 | tno = tinp
1001 | break
1002 | continue
1003 |
1004 | if cmd == "dump":
1005 | self.dump()
1006 | continue
1007 |
1008 | print_debug_help()
1009 | print(colored(f"Unknwon Cmd:'{cmd}'","red"))
1010 |
1011 | thread = self.threads[tno]
1012 | feedback = [False]
1013 | if not thread.step(feedback):
1014 | del self.threads[tno]
1015 | else:
1016 | while feedback[0]:
1017 | if not thread.step(feedback):
1018 | break
1019 | return True
1020 |
1021 | ## BEGIN MAIN CODE
1022 |
1023 | #interp = Interp(m.gr)
1024 |
1025 | #threads += [interp]
1026 |
1027 | #interp.pc = 0
1028 |
1029 | def addgr(gr,sgr,ss):
1030 | g = Group(sgr,ss,0,len(ss))
1031 | gr.children += [g]
1032 | return g
1033 |
1034 | def main():
1035 | #global threads
1036 | retval : Optional[int] = 0
1037 |
1038 | runtime = Runtime(pres.file[0], trace = not pres.no_trace, debug = pres.debug)
1039 | if pres.parse:
1040 | print(colored("Parse Complete","green"))
1041 | exit(0)
1042 |
1043 | steps = 0
1044 | while runtime.run_step():
1045 | steps += 1
1046 | if steps >= pres.steps:
1047 | print(colored("Maximum steps reached:","red"), pres.steps)
1048 | runtime.stop = True
1049 |
1050 | if "main" in runtime.interp.vars[0]:
1051 |
1052 | # Need to re-add the thread before calling main
1053 | runtime.threads += [runtime.interp]
1054 |
1055 | mainloc = runtime.interp.addr(runtime.interp.vars[0]["main"].get())
1056 | maing = runtime.interp.inst[mainloc-1][0]
1057 | main_arg_count = maing.group(1).groupCount()
1058 | expr = Group("fun","",0,0)
1059 | addgr(expr,"name","main")
1060 | vals = addgr(expr,"vals","")
1061 | if main_arg_count == 1:
1062 | alloc = addgr(vals,"alloc","")
1063 | addgr(alloc,"qual","const")
1064 | array = addgr(alloc,"array","")
1065 | for a in pres.opts:
1066 | addgr(array,"str",'"'+a+'"')
1067 | runtime.interp.start_call(expr,0)
1068 | while runtime.run_step():
1069 | steps += 1
1070 | if steps >= pres.steps:
1071 | print(colored("Maximum steps reached:","red"),pres.steps)
1072 | runtime.stop = True
1073 | if runtime.stop:
1074 | runtime.dump()
1075 | retval = 123
1076 | else:
1077 | retval = toint(runtime.interp.loads[-1][0])
1078 |
1079 | print(colored("Program Steps:","green"),steps)
1080 | if retval is None:
1081 | return 0
1082 | else:
1083 | return retval
1084 |
1085 | if __name__ == "__main__":
1086 | rc = main()
1087 | exit(rc)
1088 |
--------------------------------------------------------------------------------