├── 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 | Image of the goddess Mnemosyne 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 | --------------------------------------------------------------------------------