From cca0eb2117a98695a0280adbc1693b3c92cfab7a Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 6 Oct 2021 23:25:41 +0200 Subject: Implemented the interpreter --- README.md | 4 +- src/defaultIO.nim | 2 +- src/interpreter.nim | 33 ++++++ src/nimfunge98.nim | 44 +++++++ src/pointer.nim | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 src/interpreter.nim create mode 100644 src/nimfunge98.nim create mode 100644 src/pointer.nim diff --git a/README.md b/README.md index 311565d..f0c07e4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ This repository contains code for a nim program that can interpret a valid [Funge-98](https://github.com/catseye/Funge-98/blob/master/doc/funge98.markdown) program. It will soon pass the [mycology test suite](https://github.com/Deewiant/Mycology). Current limitations are : -- it is not finished! - currently does not implement any fingerprints - does not implement concurrent execution with the `t` command - does not implement file I/O with the `i` and `o` commands @@ -70,4 +69,7 @@ To debug these particular tests, use : ``` nim c --debugger:on --parallelBuild:1 --debuginfo --linedir:on tests/stack.nim gdb tests/stack +set args XXXXX-if-necessary +b src/truc.nim:123 +r ``` diff --git a/src/defaultIO.nim b/src/defaultIO.nim index 52bcb83..ccf4476 100644 --- a/src/defaultIO.nim +++ b/src/defaultIO.nim @@ -37,4 +37,4 @@ proc defaultCharacterOutput*(v: int) = discard stdout.writeChars(@[v.char()], 0, 1) proc defaultDecimalOutput*(v: int) = - stdout.write(&"{v}") + stdout.write(&"{v} ") diff --git a/src/interpreter.nim b/src/interpreter.nim new file mode 100644 index 0000000..56313b5 --- /dev/null +++ b/src/interpreter.nim @@ -0,0 +1,33 @@ +import field +import pointer + +type + Interpreter = object + f: ref Field + p: ref Pointer + argv: seq[string] + +func NewInterpreter*(f: ref Field, p: ref Pointer): ref Interpreter = + new(result) + result.f = f + result.p = p + +proc Step*(i: var Interpreter): ref int = + var prev: ref Pointer + var p = i.p + while p != nil: + let (d, v) = p[].Exec(i.f[]) + if v != nil: + return v + if d: + if prev == nil: + i.p = p.next + else: + prev.next = p.next + p = p.next + +proc Run*(i: var Interpreter): int = + while i.p != nil: + let v = i.Step() + if v != nil: + return v[] diff --git a/src/nimfunge98.nim b/src/nimfunge98.nim new file mode 100644 index 0000000..6e6efb9 --- /dev/null +++ b/src/nimfunge98.nim @@ -0,0 +1,44 @@ +import field +import interpreter +import pointer + +import os +import parseopt +import strformat + +proc Usage(i: int = 0) = + let filename = getAppFilename().extractFilename() + echo fmt"""Usage of {filename}: + -f string b98 file to interpret + -h display this help message + """ + if i != 0: + quit i + +var filename: string + +for kind, key, value in getOpt(): + case kind + of cmdArgument: + if filename != "": + echo "Invalid argument: ", key + Usage(1) + filename = key + of cmdLongOption, cmdShortOption: + case key + of "h": + Usage() + else: + echo "Unknown option: ", key + Usage(1) + of cmdEnd: + discard + +var f = Load(filename) +if f == nil: + echo "Failed to load ", filename + quit 1 +let argv = @[filename] +var p = NewPointer(argv=argv) +let v = NewInterpreter(f, p)[].Run() +quit v diff --git a/src/pointer.nim b/src/pointer.nim new file mode 100644 index 0000000..cbf13b7 --- /dev/null +++ b/src/pointer.nim @@ -0,0 +1,322 @@ +import defaultIO +import field +import stackStack + +import math +import os +import random +import times + +randomize() + +type + Pointer* = object + x, y: int # The pointer's positiont + dx, dy: int # The pointer's traveling delta + sox, soy: int # The storage offset + stringMode, lastCharWasSpace: bool # The string mode flags + ss: ref StackStack + next*: ref Pointer # the next element for multi-threaded funge-98 + characterInput*: proc(): int + decimalInput*: proc(): int + characterOutput*: proc(v: int) + decimalOutput*: proc(v: int) + argv: seq[string] + +func NewPointer*(next: ref Pointer = nil, argv: seq[string]): ref Pointer = + new(result) + result.dx = 1 + result.ss = NewStackStack() + result.next = next + result.characterInput = defaultCharacterInput + result.decimalInput = defaultDecimalInput + result.characterOutput = defaultCharacterOutput + result.decimalOutput = defaultDecimalOutput + result.argv = argv + +#func Split(p: var Pointer) = +# # TODO check the spec, maybe we need to duplicate the stack? +# p.next = NewPointer(p.next) + +func Step(p: var Pointer, f: Field) = + (p.x, p.y) = f.Step((p.x, p.y), (p.dx, p.dy)) + +func StepAndGet(p: var Pointer, f: Field): int = + p.Step(f) + return f.Get(p.x, p.y) + +func Reverse(p: var Pointer) = + p.dx = -p.dx; p.dy = -p.dy + +proc Redirect(p: var Pointer, c: int): bool = + ## Redirects the pointer according to c's value. + ## Returns true if a redirect has been performed + case c: + of int('^'): p.dx = 0; p.dy = -1 + of int('>'): p.dx = 1; p.dy = 0 + of int('v'): p.dx = 0; p.dy = 1 + of int('<'): p.dx = -1; p.dy = 0 + of int('?'): + const directions = [0, -1, 1, 0, 0, 1, -1, 0] + let r = 2 * rand(3) + p.dx = directions[r]; p.dy = directions[r+1] + of int('['): (p.dx, p.dy) = (p.dy, -p.dx) + of int(']'): (p.dx, p.dy) = (-p.dy, p.dx) + of int('r'): p.Reverse() + of int('x'): p.dy = p.ss[].Pop(); p.dx = p.ss[].Pop() + else: return false + return true + +proc Eval(p: var Pointer, f: var Field, c: int): (bool, ref int) = + ## Executes the instruction on the field + ## Returns true if the pointer terminated, and a return code if + ## the program should terminate completely + case c: + of int('@'): return (true, nil) + of int('z'): discard + of int('#'): p.Step(f) + of int('j'): + let n = p.ss[].Pop() + if n > 0: + for j in 0.. 0: + p.x = x; p.y = y + if v != int(' ') and v != int(';'): + if v == int('q') or v == int('@'): + return p.Eval(f, v) + for i in 0.. b: + p.ss[].Push(1) + else: + p.ss[].Push(0) + of int('_'): + if p.ss[].Pop() == 0: + p.dx = 1 + else: + p.dx = -1 + p.dy = 0 + of int('|'): + p.dx = 0 + if p.ss[].Pop() == 0: + p.dy = 1 + else: + p.dy = -1 + of int('w'): + let (a, b) = p.ss[].PopVector() + if a < b: + (p.dx, p.dy) = (p.dy, -p.dx) + elif a > b: + (p.dx, p.dy) = (-p.dy, p.dx) + of int('+'): + let (a, b) = p.ss[].PopVector() + p.ss[].Push(a+b) + of int('*'): + let (a, b) = p.ss[].PopVector() + p.ss[].Push(a*b) + of int('-'): + let (a, b) = p.ss[].PopVector() + p.ss[].Push(a-b) + of int('/'): + let (a, b) = p.ss[].PopVector() + if b == 0: + p.ss[].Push(0) + else: + p.ss[].Push(a div b) + of int('%'): + let (a, b) = p.ss[].PopVector() + if b == 0: + p.ss[].Push(0) + else: + p.ss[].Push(a mod b) + of int('"'): + p.stringMode = true + of int('\''): + p.ss[].Push(p.StepAndGet(f)) + of int('s'): + p.Step(f) + f.Set(p.x, p.y, p.ss[].Pop()) + of int('$'): + discard p.ss[].Pop() + of int(':'): + p.ss[].Duplicate() + of int('\\'): + p.ss[].Swap() + of int('n'): + p.ss[].Clear() + of int('{'): + p.ss[].Begin((p.sox, p.soy)) + p.sox = p.x + p.dx + p.soy = p.y + p.dy + of int('}'): + var v: tuple[x, y: int] + if p.ss[].End(v): + p.Reverse() + p.sox = v.x; p.soy = v.y + of int('u'): + if p.ss[].Under(): + p.Reverse() + of int('g'): + let (x, y) = p.ss[].PopVector() + p.ss[].Push(f.Get(x+p.sox, y+p.soy)) + of int('p'): + let (x, y) = p.ss[].PopVector() + let v = p.ss[].Pop() + f.Set(x+p.sox, y+p.soy, v) + of int('.'): + p.decimalOutput(p.ss[].Pop()) + of int(','): + p.characterOutput(p.ss[].Pop()) + of int('&'): + p.ss[].Push(p.decimalInput()) + of int('~'): + p.ss[].Push(p.characterInput()) + of int('y'): + let n = p.ss[].Pop() + let now = now() + let (x, y, lx, ly) = f.GetSize() + let (height, heights) = p.ss[].GetHeights() + # 20 + for key, value in envPairs(): + case key + of "LC_ALL": discard + of "PWD": discard + of "PATH": discard + of "DISPLAY": discard + of "USER": discard + of "TERM": discard + of "LANG": discard + of "HOME": discard + of "EDITOR": discard + of "SHELL": discard + else: continue + p.ss[].Push(0) + for i in value.len-1..0: + p.ss[].Push(int(value[i])) + # 19 + p.ss[].PushVector((0, 0)) + for i in 0.. 0: + p.ss[].YCommandPick(n, heights[0]) + of int('('): + let n = p.ss[].Pop() + var v = 0 + for i in 0..= int('0') and c <= int('9'): + p.ss[].Push(c - int('0')) + elif c >= int('a') and c <= int('f'): + p.ss[].Push(c - int('a') + 10) + else: + p.Reverse() + return (false, nil) + +proc Exec*(p: var Pointer, f: var Field): (bool, ref int) = + ## Advances to the next instruction of the field and executes it + ## Returns true if the pointer terminated, and a return code if + ## the program should terminate completely + var c = f.Get(p.x, p.y) + if p.stringMode: + if p.lastCharWasSpace: + while c == int(' '): + c = p.StepAndGet(f) + p.lastCharWasSpace = false + if c == int('"'): + p.stringMode = false + else: + if c == int(' '): + p.lastCharWasSpace = true + p.ss[].Push(c) + else: + var jumpingMode = false + while jumpingMode or c == int(' ') or c == int(';'): + if c == int(';'): + jumpingMode = not jumpingMode + c = p.StepAndGet(f) + result = p.Eval(f, c) + p.Step(f) + return result -- cgit v1.2.3