From 9dc429c92d6585394e6d89bb90c21b7e302946f8 Mon Sep 17 00:00:00 2001 From: prod2 <95874442+prod2@users.noreply.github.com> Date: Mon, 7 Feb 2022 05:35:07 +0100 Subject: [PATCH] natives --- benchmarks/benchmarkgen.nim | 2 +- benchmarks/factorial.nds | 2 +- benchmarks/fib.nds | 2 +- examples/bf.nds | 36 +++++------ examples/test.nds | 5 -- src/ndspkg/chunk.nim | 3 - src/ndspkg/compiler.nim | 17 ----- src/ndspkg/lib/io.nim | 33 ++++++++++ src/ndspkg/lib/main.nim | 6 ++ src/ndspkg/lib/typeutils.nim | 36 +++++++++++ src/ndspkg/scanner.nim | 7 +-- src/ndspkg/types/closure.nim | 2 +- src/ndspkg/types/hashtable.nim | 2 +- src/ndspkg/types/native.nim | 27 ++++++++ src/ndspkg/types/ndlist.nim | 6 +- src/ndspkg/types/ndstring.nim | 6 +- src/ndspkg/types/stack.nim | 12 ++-- src/ndspkg/types/value.nim | 27 ++++---- src/ndspkg/vm.nim | 111 +++++++++++++++------------------ tests/ampersand.nds | 4 +- tests/break.nds | 26 ++++---- tests/closures.nds | 56 ++++++++--------- tests/collections.nds | 2 +- tests/controlflow.nds | 58 ++++++++--------- tests/precedence.nds | 10 +-- tests/shadowing.nds | 6 +- 26 files changed, 284 insertions(+), 220 deletions(-) delete mode 100644 examples/test.nds create mode 100644 src/ndspkg/lib/io.nim create mode 100644 src/ndspkg/lib/main.nim create mode 100644 src/ndspkg/lib/typeutils.nim create mode 100644 src/ndspkg/types/native.nim diff --git a/benchmarks/benchmarkgen.nim b/benchmarks/benchmarkgen.nim index 160872d..2d41312 100644 --- a/benchmarks/benchmarkgen.nim +++ b/benchmarks/benchmarkgen.nim @@ -7,7 +7,7 @@ proc genAssignIncr100k = var src = "var x = 0;\n" for i in countup(0, 99999): src &= "x = x + 1;\n" - src &= "print x;\n" + src &= "print (x);\n" path.writeFile(src) genAssignIncr100k() \ No newline at end of file diff --git a/benchmarks/factorial.nds b/benchmarks/factorial.nds index 4f98ac3..fda6ffd 100644 --- a/benchmarks/factorial.nds +++ b/benchmarks/factorial.nds @@ -11,4 +11,4 @@ while (i < 50000) { res = res + fact(50); i = i + 1; }; -print res; +print (res); diff --git a/benchmarks/fib.nds b/benchmarks/fib.nds index 573dcc5..dadc8c3 100644 --- a/benchmarks/fib.nds +++ b/benchmarks/fib.nds @@ -3,4 +3,4 @@ var fib = funct(n) else :result = fib(n-1) + fib(n-2) ; -print fib(37); \ No newline at end of file +print (fib(37)); \ No newline at end of file diff --git a/examples/bf.nds b/examples/bf.nds index ddca6f8..a85a23c 100644 --- a/examples/bf.nds +++ b/examples/bf.nds @@ -3,7 +3,7 @@ var main = funct() { var src = ">++++++++++[<++++++++++>-]<->>>>>+++[>+++>+++<<-]<<<<+<[>[>+>+<<-]>>[-<<+>>]++++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[[-]>>>>>>[[-]<++++++++++<->>]<-[>+>+<<-]>[<+>-]+>[[-]<->]<<<<<<<<<->>]<[>+>+<<-]>>[-<<+>>]+>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<<[>>+>+<<<-]>>>[-<<<+>>>]++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[>+<[-]]<[>>+<<[-]]>>[<<+>>[-]]<<<[>>+>+<<<-]>>>[-<<<+>>>]++++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[>+<[-]]<[>>+<<[-]]>>[<<+>>[-]]<<[[-]>>>++++++++[>>++++++<<-]>[<++++++++[>++++++<-]>.<++++++++[>------<-]>[<<+>>-]]>.<<++++++++[>>------<<-]<[->>+<<]<++++++++[<++++>-]<.>+++++++[>+++++++++<-]>+++.<+++++[>+++++++++<-]>.+++++..--------.-------.++++++++++++++>>[>>>+>+<<<<-]>>>>[-<<<<+>>>>]>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<<<[>>>+>+<<<<-]>>>>[-<<<<+>>>>]+>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<<[>>+<<[-]]>[>+<[-]]++>>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<+<[[-]>-<]>[<<<<<<<.>>>>>>>[-]]<<<<<<<<<.>>----.---------.<<.>>----.+++..+++++++++++++.[-]<<[-]]<[>+>+<<-]>>[-<<+>>]+>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<<[>>+>+<<<-]>>>[-<<<+>>>]++++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[>+<[-]]<[>>+<<[-]]>>[<<+>>[-]]<<[[-]>++++++++[<++++>-]<.>++++++++++[>+++++++++++<-]>+.-.<<.>>++++++.------------.---.<<.>++++++[>+++<-]>.<++++++[>----<-]>++.+++++++++++..[-]<<[-]++++++++++.[-]]<[>+>+<<-]>>[-<<+>>]+++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[[-]++++++++++. >+++++++++[>+++++++++<-]>+++.+++++++++++++.++++++++++.------.<++++++++[>>++++<<-]>>.<++++++++++.-.---------.>.<-.+++++++++++.++++++++.---------.>.<-------------.+++++++++++++.----------.>.<++++++++++++.---------------.<+++[>++++++<-]>..>.<----------.+++++++++++.>.<<+++[>------<-]>-.+++++++++++++++++.---.++++++.-------.----------.[-]>[-]<<<.[-]]<[>+>+<<-]>>[-<<+>>]++++>+<[-<->]<[[-]>>-<<]>>[[-]<<+>>]<<[[-]++++++++++.[-]<[-]>]<+<]"; var pos = 0; var len = #src; - var char; + var chr; var depth = 0; // input @@ -23,55 +23,55 @@ var main = funct() { { @mainloop while (pos < len) { - char = src[pos]; + chr = src[pos]; - if (char == "<") + if (chr == "<") if (ptr > 0) ptr = ptr - 1 else ptr = ptrmax ; - if (char == ">") + if (chr == ">") if (ptr < ptrmax) ptr = ptr + 1 else ptr = 0 ; - if (char == "+") + if (chr == "+") if (tape[ptr] < 255) tape[ptr] = tape[ptr] + 1 else tape[ptr] = 0 ; - if (char == "-") + if (chr == "-") if (tape[ptr] > 0) tape[ptr] = tape[ptr] - 1 else tape[ptr] = 255 ; - if (char == ".") - write(chr(tape[ptr])) + if (chr == ".") + write(char(tape[ptr])) ; - if (char == ",") { - tape[ptr] = int(input[inppos]); + if (chr == ",") { + tape[ptr] = byte(input[inppos]); inppos = inppos + 1; }; - if (char == "[") { + if (chr == "[") { if (tape[ptr] == 0) { depth = depth + 1; while (depth > 0) { pos = pos + 1; - char = src[pos]; + chr = src[pos]; depth = depth + - if (char == "[") + if (chr == "[") 1 - else if (char == "]") + else if (chr == "]") -1 else 0 @@ -80,16 +80,16 @@ var main = funct() { }; }; - if (char == "]") { + if (chr == "]") { if (tape[ptr] != 0) { depth = depth + 1; while (depth > 0) { pos = pos - 1; - char = src[pos]; + chr = src[pos]; depth = depth + - if (char == "]") + if (chr == "]") 1 - else if (char == "[") + else if (chr == "[") -1 else 0 diff --git a/examples/test.nds b/examples/test.nds deleted file mode 100644 index 32e0377..0000000 --- a/examples/test.nds +++ /dev/null @@ -1,5 +0,0 @@ -var argcap = funct(n) - :result = funct() print n -; - -argcap(5)(); diff --git a/src/ndspkg/chunk.nim b/src/ndspkg/chunk.nim index e68b32c..6d58a74 100644 --- a/src/ndspkg/chunk.nim +++ b/src/ndspkg/chunk.nim @@ -19,7 +19,6 @@ type opJumpIfFalse, opJump, opLoop, opJumpIfFalsePop, # jumps opCreateList, opCreateTable, # collection creation opLen, opSetIndex, opGetIndex, # collection operators - opPrint, opChr, opInt, opPutchar, # temporary opcodes for the brainfuck interpreter TODO move to native funcs Chunk* = object code*: seq[uint8] @@ -92,13 +91,11 @@ proc writeConstant*(ch: var Chunk, constant: NdValue, line: int): int = const simpleInstructions = { opReturn, opPop, - opPrint, opNegate, opNot, opAdd, opSubtract, opMultiply, opDivide, opEqual, opGreater, opLess, opTrue, opFalse, opNil, opLen, opSetIndex, opGetIndex, - opChr, opInt, opPutchar, } const constantInstructions = { opConstant, diff --git a/src/ndspkg/compiler.nim b/src/ndspkg/compiler.nim index 6c03ffb..b870ba8 100644 --- a/src/ndspkg/compiler.nim +++ b/src/ndspkg/compiler.nim @@ -551,19 +551,10 @@ proc unary(comp: Compiler) = comp.writeChunk(0, opNegate) of tkBang: comp.writeChunk(0, opNot) - of tkInt: - comp.writeChunk(0, opInt) - of tkChr: - comp.writeChunk(0, opChr) - of tkPutch: - comp.writeChunk(0, opPutchar) else: discard # unreachable tkBang.genRule(unary, nop, pcNone) -tkInt.genRule(unary, nop, pcNone) -tkChr.genRule(unary, nop, pcNone) -tkPutch.genRule(unary, nop, pcNone) proc binary(comp: Compiler) = let opType = comp.previous.tokenType @@ -663,14 +654,6 @@ proc orExpr(comp: Compiler) = tkOr.genRule(nop, orExpr, pcOr) -proc debugExpr(comp: Compiler) = - comp.expression() - when debugCompiler: - debugEcho &"debug expression, current stackindex: {comp.stackIndex}" - comp.writeChunk(0, opPrint) - -tkPrint.genRule(debugExpr, nop, pcNone) - proc parseWhile(comp: Compiler) = comp.writeChunk(1, opNil) # return value diff --git a/src/ndspkg/lib/io.nim b/src/ndspkg/lib/io.nim new file mode 100644 index 0000000..1247d33 --- /dev/null +++ b/src/ndspkg/lib/io.nim @@ -0,0 +1,33 @@ +import ../types/value +import ../types/native +import ../types/ndstring +import bitops + +proc natRead(args: seq[NdValue], retval: var NdValue): NatReturn = + retval = stdin.readLine().fromNimString() + return natOk + +proc natPrint(args: seq[NdValue], retval: var NdValue): NatReturn = + if args.len() == 0: + retval = fromNil() + return natOk + for i, arg in args.pairs: + write stdout, $arg + if i < args.high(): + write stdout, " " + write stdout, "\n" + retval = args[0]; + return natOk + +proc natWrite(args: seq[NdValue], retval: var NdValue): NatReturn = + if args.len() != 1: + return natError("Write expects exactly 1 argument, got " & $args.len() & ".") + let arg = args[0] + write stdout, $arg + retval = arg; + return natOk + +proc constructIo* = + defNative("read", natRead) + defNative("print", natPrint) + defNative("write", natWrite) diff --git a/src/ndspkg/lib/main.nim b/src/ndspkg/lib/main.nim new file mode 100644 index 0000000..d6b2e43 --- /dev/null +++ b/src/ndspkg/lib/main.nim @@ -0,0 +1,6 @@ +import io +import typeutils + +proc constructStdlib* = + constructIo() + constructTypeutils() \ No newline at end of file diff --git a/src/ndspkg/lib/typeutils.nim b/src/ndspkg/lib/typeutils.nim new file mode 100644 index 0000000..56a96d0 --- /dev/null +++ b/src/ndspkg/lib/typeutils.nim @@ -0,0 +1,36 @@ +import ../types/value +import ../types/native +import ../types/ndstring +import bitops + +proc natChar(args: seq[NdValue], retval: var NdValue): NatReturn = + if args.len() != 1: + return natError("Char expects 1 argument.") + let arg = args[0] + if not arg.isFloat(): + return natError("Char expects a number.") + let floatval = arg.asFloat() + if floatval > 255f or floatval < 0f: + return natError("Char got a number out of range.") + let chr = floatval.char() + retval = (chr).newString().fromNdString() + return natOk + +proc natByte(args: seq[NdValue], retval: var NdValue): NatReturn = + if args.len() != 1: + return natError("Byte expects 1 argument.") + let arg = args[0] + if not arg.isString(): + return natError("Byte expects a string.") + if arg.asString().getLength() != 1: + return natError("Byte expects a single character string.") + + let byt = arg.asString().getIndexAsChar(0).float() + retval = byt.fromFloat() + return natOk + + +proc constructTypeutils* = + defNative("char", natChar) + defNative("byte", natByte) + diff --git a/src/ndspkg/scanner.nim b/src/ndspkg/scanner.nim index b1754c7..eee39ea 100644 --- a/src/ndspkg/scanner.nim +++ b/src/ndspkg/scanner.nim @@ -18,8 +18,7 @@ type tkHashtag, tkAmpersand, tkIdentifier, tkString, tkNumber, tkAnd, tkElse, tkFalse, tkFor, tkFunct, tkGoto, tkIf, tkNil, - tkOr, tkPrint, tkLabel, tkBreak, tkTrue, tkVar, tkWhile, - tkChr, tkInt, tkPutch, + tkOr, tkLabel, tkBreak, tkTrue, tkVar, tkWhile, tkError, tkEof Token* = object @@ -122,13 +121,9 @@ const keywords = { "if": tkIf, "nil": tkNil, "or": tkOr, - "print": tkPrint, "true": tkTrue, "var": tkVar, "while": tkWhile, - "int": tkInt, - "chr": tkChr, - "write": tkPutch, }.toTable proc canStartIdent(chr: char): bool = diff --git a/src/ndspkg/types/closure.nim b/src/ndspkg/types/closure.nim index 7911725..a4d0a60 100644 --- a/src/ndspkg/types/closure.nim +++ b/src/ndspkg/types/closure.nim @@ -23,7 +23,7 @@ proc newClosure*[T](start: ptr uint8, upvalueCount: int): Closure[T] = for i in 0 .. upvalueCount: result.upvalues[i] = nil -proc getIp*[T](clos: Closure[T]): ptr uint8 {.inline.} = +proc getIp*[T](clos: Closure[T]): ptr uint8 = clos.start proc set*[T](clos: Closure[T], index: int, val: Upvalue[T]) = diff --git a/src/ndspkg/types/hashtable.nim b/src/ndspkg/types/hashtable.nim index 4008467..ca4075c 100644 --- a/src/ndspkg/types/hashtable.nim +++ b/src/ndspkg/types/hashtable.nim @@ -52,7 +52,7 @@ proc findEntry[U, V](entries: ptr UncheckedArray[Entry[U, V]], cap: int, key: U) return entry index = (index + 1).bitand(cap - 1) -proc grow[U, V](tbl: var Table[U, V]): int {.inline.} = +proc grow[U, V](tbl: var Table[U, V]): int = ## Calculates the new capacity if tbl.cap > 0: tbl.cap * 2 diff --git a/src/ndspkg/types/native.nim b/src/ndspkg/types/native.nim new file mode 100644 index 0000000..49ee694 --- /dev/null +++ b/src/ndspkg/types/native.nim @@ -0,0 +1,27 @@ +type + NatReturn* = object + ok*: bool + msg*: string + + Native* = uint32 + + natProc = proc (args: seq[uint], retval: var uint): NatReturn + +# NatReturn misc + +proc natError*(msg: string): NatReturn = + NatReturn(ok: false, msg: msg) + +const natOk* = NatReturn(ok: true) + +# natives + +var natives*: seq[natProc] +var nativeNames*: seq[string] + +proc defNative*(name: string, action: natProc) = + natives.add(action) + nativeNames.add(name) + +proc callNative*(native: Native, args: seq[uint], retval: var uint): NatReturn = + natives[native.int](args, retval) \ No newline at end of file diff --git a/src/ndspkg/types/ndlist.nim b/src/ndspkg/types/ndlist.nim index 25cf1ea..3a89f22 100644 --- a/src/ndspkg/types/ndlist.nim +++ b/src/ndspkg/types/ndlist.nim @@ -32,7 +32,7 @@ proc free*[T](list: var List[T]) = ## dealloc's the list object list.dealloc() -proc grow[T](list: var List[T]) {.inline.} = +proc grow[T](list: var List[T]) = ## growth the list's capacity let newcap = if list.cap == 0: startCap else: list.cap * growthFactor let size = newcap * sizeof(T) @@ -43,7 +43,7 @@ proc grow[T](list: var List[T]) {.inline.} = list.entries = cast[ptr UncheckedArray[T]](list.entries.realloc(size)) list.cap = newcap -proc add*[T](list: var List[T], item: T) {.inline.} = +proc add*[T](list: var List[T], item: T) = if list == nil or list.len == list.cap: list.grow() list.entries[list.len] = item @@ -75,7 +75,7 @@ proc setIndexNeg*[T](list: List[T], index: int, item: T) = raise newException(Defect, "Attempt to setIndexNeg with an index out of bounds.") list.entries[list.len - index] = item -proc getLength*[T](list: List[T]): int {.inline.} = +proc getLength*[T](list: List[T]): int = if list == nil: 0 else: diff --git a/src/ndspkg/types/ndstring.nim b/src/ndspkg/types/ndstring.nim index af06009..1c128f2 100644 --- a/src/ndspkg/types/ndstring.nim +++ b/src/ndspkg/types/ndstring.nim @@ -11,7 +11,7 @@ proc free*(ndStr: var NdString) = # hashes -proc fnv1a*(ndStr: NdString): int {.inline.} = +proc fnv1a*(ndStr: NdString): int = return ndStr.hash.int #var hash = 2166136261'u32 #for i in countup(0, ndStr.len.int - 1): @@ -19,14 +19,14 @@ proc fnv1a*(ndStr: NdString): int {.inline.} = # hash *= 16777619 #return hash.int -proc fnv1a*(str: string): int {.inline.} = # SHOULD RETURN THE SAME HASH AS AN NDSTRING WITH THE SAME CONTENTS +proc fnv1a*(str: string): int = # SHOULD RETURN THE SAME HASH AS AN NDSTRING WITH THE SAME CONTENTS var hash = 2166136261'u32 for i in countup(0, str.len - 1): hash = hash xor (str[i]).uint32 hash *= 16777619 return hash.int -proc fnv1a*(str: char): int {.inline.} = # SHOULD RETURN THE SAME AS A 1 LENGTH STRING +proc fnv1a*(str: char): int = # SHOULD RETURN THE SAME AS A 1 LENGTH STRING var hash = 2166136261'u32 hash = hash xor str.uint32 hash *= 16777619 diff --git a/src/ndspkg/types/stack.nim b/src/ndspkg/types/stack.nim index 25cfac7..43be89e 100644 --- a/src/ndspkg/types/stack.nim +++ b/src/ndspkg/types/stack.nim @@ -26,13 +26,13 @@ proc free*[T](stack: var Stack[T]) = stack.start = nil stack.top = nil -proc grow[T](stack: var Stack[T], len: int) {.inline.} = +proc grow[T](stack: var Stack[T], len: int) = ## growth the stack's capacity and increments the top's index by one stack.cap *= growthFactor stack.start = cast[ptr T](realloc(stack.start, stack.cap * sizeof(T))) stack.top = stack.start.padd(len * sizeof(T)) -proc shrink[T](stack: var Stack[T]) {.inline.} = +proc shrink[T](stack: var Stack[T]) = discard template high*[T](stack: Stack[T]): int = @@ -41,7 +41,7 @@ template high*[T](stack: Stack[T]): int = template len*[T](stack: Stack[T]): int = stack.high() + 1 -proc push*[T](stack: var Stack[T], item: T) {.inline.} = +proc push*[T](stack: var Stack[T], item: T) = let len = stack.len() if len == stack.cap: stack.grow(len) @@ -52,20 +52,20 @@ proc push*[T](stack: var Stack[T], item: T) {.inline.} = template add*[T](stack: var Stack[T], item: T) = stack.push(item) -proc pop*[T](stack: var Stack[T]): T {.inline.} = +proc pop*[T](stack: var Stack[T]): T = when boundsChecks: if stack.top == nil or stack.top.pless(stack.start): raise newException(Defect, "Stacktop is nil or smaller than start.") result = stack.top[] stack.top = stack.top.psub(sizeof(T)) -proc peek*[T](stack: Stack[T]): var T {.inline.} = +proc peek*[T](stack: Stack[T]): var T = when boundsChecks: if stack.top == nil or stack.top.pless(stack.start): raise newException(Defect, "Stacktop is nil or smaller than start.") stack.top[] -proc settip*[T](stack: var Stack[T], newtip: T) {.inline.} = +proc settip*[T](stack: var Stack[T], newtip: T) = when boundsChecks: if stack.top == nil or stack.top.pless(stack.start): raise newException(Defect, "Stacktop is nil or smaller than start") diff --git a/src/ndspkg/types/value.nim b/src/ndspkg/types/value.nim index 693e3ae..1b74813 100644 --- a/src/ndspkg/types/value.nim +++ b/src/ndspkg/types/value.nim @@ -6,13 +6,11 @@ import ndstring import ndlist import hashtable import closure +import native type NdValue* = uint - NatReturn* = object - ok*: bool - msg*: string # NaN boxing constants @@ -29,7 +27,7 @@ type # if bit 63 is 1: # 00 -> list # 01 -> table - # 10 -> native funct (to be implemented later) + # 10 -> native funct # 11 -> unused for now const qNan* = 0x7ffc000000000000'u @@ -40,14 +38,13 @@ const ndFalse* = 0x7ffc000000000003'u # 0111 1111 1111 11*01* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 const tagString* = 0x7ffd000000000000'u - # 0111 1111 1111 11*10* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 const tagFunct* = 0x7ffe000000000000'u - +# you can imagine these now... const tagClosure* = 0x7fff000000000000'u - const tagList* = 0xfffc000000000000'u const tagTable* = 0xfffd000000000000'u +const tagNative* = 0xfffe000000000000'u const mask48* = 0xffff000000000000'u @@ -77,6 +74,9 @@ template isList*(val: NdValue): bool = template isTable*(val: NdValue): bool = val.bitand(mask48) == tagTable +template isNative*(val: NdValue): bool = + val.bitand(mask48) == tagNative + # these assume that the type has been previously determined template asBool*(val: NdValue): bool = val == ndTrue @@ -99,6 +99,9 @@ template asList*(val: NdValue): List[NdValue] = template asTable*(val: NdValue): NdTable[NdValue, NdValue] = cast[NdTable[NdValue, NdValue]](val.bitand(mask48.bitnot())) +template asNative*(val: NdValue): Native = + cast[Native](val.bitand(mask48.bitnot())) + template fromNil*(): NdValue = ndNil @@ -126,6 +129,9 @@ template fromList*(val: List[NdValue]): NdValue = template fromTable*(val: NdTable[NdValue, NdValue]): NdValue = cast[uint](val).bitor(tagTable) +template fromNative*(val: Native): NdValue = + cast[uint](val).bitor(tagNative) + # for hashtables @@ -190,13 +196,6 @@ proc friendlyType*(val: NdValue): string = else: "unknown" -# NatReturn misc - -proc natError*(msg: string): NatReturn {.inline.} = - NatReturn(ok: false, msg: msg) - -const natOk* = NatReturn(ok: true) - # OPERATIONS proc negate*(val: var NdValue): NatReturn {.inline.} = diff --git a/src/ndspkg/vm.nim b/src/ndspkg/vm.nim index 2a657ba..a3015f3 100644 --- a/src/ndspkg/vm.nim +++ b/src/ndspkg/vm.nim @@ -11,6 +11,9 @@ import types/value import types/hashtable import types/ndlist import types/closure +import types/native + +import lib/main when debugVM: import terminal @@ -78,7 +81,17 @@ proc run*(chunk: Chunk): InterpretResult = elif funct.isClosure(): frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip, closure: funct.asClosure())) ip = funct.asClosure().getIp() + elif funct.isNative(): + var args: seq[NdValue] = newSeq[NdValue](argcount) + if argcount > 0: + copyMem(args[0].unsafeAddr, stack.getIndexNeg(argcount - 1).addr, argcount * sizeof(NdValue)) + stack.deleteTopN(argcount) + let res = callNative(funct.asNative(), args, stack.peek()) + if not res.ok: + runtimeError(res.msg) + error else: + runtimeError("Attempt to call a non-funct (a defunct?).") # here is a bad defunct joke error proc captureUpvalue(location: ptr NdValue): Upvalue[NdValue] = @@ -116,6 +129,13 @@ proc run*(chunk: Chunk): InterpretResult = upval.location = upval.closed.addr openUpvalues = upval.next + # initialize globals + constructStdlib() + for i in 0 .. natives.high(): + let native = i.uint32.fromNative() + let name = nativeNames[i].fromNimString() + discard globals.tableSet(name, native) + while true: {.computedgoto.} # See https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma @@ -222,8 +242,6 @@ proc run*(chunk: Chunk): InterpretResult = if not res.ok: runtimeError(res.msg) break - of opPrint: - echo $stack.peek() of opDefineGlobal: let name = ip.readConstant(chunk) let existed = globals.tableSet(name, stack.pop()) @@ -251,20 +269,6 @@ proc run*(chunk: Chunk): InterpretResult = of opSetLocal: let slot = ip.readDU8() stack[slot + frameBottom] = stack.peek() - of opGetUpvalue: - let slot = ip.readDU8() - let val = frames.peek().closure.get(slot).read() - when debugClosures: - echo &"CLOSURES - getupvalue got {val} from slot {slot}" - stack.push(val) - of opSetUpvalue: - let slot = ip.readDU8() - when debugClosures: - echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}" - frames.peek().closure.get(slot).write(stack.peek()) - of opCloseUpvalue: - let slot = ip.readDU8() - stack[slot + frameBottom].addr.closeUpvalues() of opJumpIfFalse: let offset = ip.readDU8() if stack.peek().isFalsey(): @@ -284,24 +288,6 @@ proc run*(chunk: Chunk): InterpretResult = let faddr: ptr uint8 = ip ip = ip.padd(offset) stack.push(faddr.fromFunct()) - of opClosure: - let upvalCount = ip.readDU8() - let funct = stack.peek() - let closure = newClosure[NdValue](funct.asFunct(), upvalCount) - stack.settip(closure.fromClosure()) - for i in countup(0, upvalCount - 1): - let slot = ip.readDU8() - let isLocal = ip.readUI8() == 0 - if isLocal: - let loc = stack[slot + frameBottom].addr - when debugClosures: - echo &"CLOSURES - opClosure: local upvalue {loc[]} from local slot {slot} to slot {i}" - closure.set(i, loc.captureUpvalue()) - else: - let val = frames.peek().closure.get(slot) - when debugClosures: - echo &"CLOSURES - opClosure: non local upvalue {val.location[]} from slot {slot} to slot {i}" - closure.set(i, val) of opCheckArity: let arity = ip.readUI8() @@ -320,7 +306,6 @@ proc run*(chunk: Chunk): InterpretResult = stack.setIndexNeg(argcount, fromNil()) # replace the function with nil: this is the return value slot funct.call(argcount): - runtimeError("Attempt to call a non-funct (a defunct?).") # here is a bad defunct joke break of opCreateList: let listLen = ip.readDU8() @@ -360,30 +345,38 @@ proc run*(chunk: Chunk): InterpretResult = if not res.ok: runtimeError(res.msg) break - of opChr: - let val = stack.peek() - if not val.isFloat(): - runtimeError("chr on not float") - break - let floatval = val.asFloat() - if floatval > 255f or floatval < 0f: - runtimeError("chr on a float out of range") - break - let chr = floatval.char() - stack.settip((chr).newString().fromNdString()) - of opInt: - let val = stack.peek() - if not val.isString(): - runtimeError("int on non string") - break - let strval = val.asString() - if strval.getLength() == 0: - runtimeError("int on empty string") - break - let code = val.asString().getIndexAsChar(0).float() - stack.settip(code.fromFloat()) - of opPutchar: - write stdout, $stack.peek() + of opGetUpvalue: + let slot = ip.readDU8() + let val = frames.peek().closure.get(slot).read() + when debugClosures: + echo &"CLOSURES - getupvalue got {val} from slot {slot}" + stack.push(val) + of opSetUpvalue: + let slot = ip.readDU8() + when debugClosures: + echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}" + frames.peek().closure.get(slot).write(stack.peek()) + of opCloseUpvalue: + let slot = ip.readDU8() + stack[slot + frameBottom].addr.closeUpvalues() + of opClosure: + let upvalCount = ip.readDU8() + let funct = stack.peek() + let closure = newClosure[NdValue](funct.asFunct(), upvalCount) + stack.settip(closure.fromClosure()) + for i in countup(0, upvalCount - 1): + let slot = ip.readDU8() + let isLocal = ip.readUI8() == 0 + if isLocal: + let loc = stack[slot + frameBottom].addr + when debugClosures: + echo &"CLOSURES - opClosure: local upvalue {loc[]} from local slot {slot} to slot {i}" + closure.set(i, loc.captureUpvalue()) + else: + let val = frames.peek().closure.get(slot) + when debugClosures: + echo &"CLOSURES - opClosure: non local upvalue {val.location[]} from slot {slot} to slot {i}" + closure.set(i, val) when profileInstructions: durations[ins] += getMonoTime() - startTime diff --git a/tests/ampersand.nds b/tests/ampersand.nds index 3d7d2d7..5d719fb 100644 --- a/tests/ampersand.nds +++ b/tests/ampersand.nds @@ -6,7 +6,7 @@ x[0] = 0 & [2] = 0; x[5] = 1 & [6] = 0 & [7] = 3 & [7] = 2; //expect:@[ 0.0, 2.0, 0.0, 4.0, 5.0, 1.0, 0.0, 2.0 ] -print x; +print (x); // not very useful but still must be correct behavior tests: @@ -14,5 +14,5 @@ print x; var y = 5 + 1 & * 3; //expect:18.0 -print y; +print (y); diff --git a/tests/break.nds b/tests/break.nds index cbb37de..9fdfe2d 100644 --- a/tests/break.nds +++ b/tests/break.nds @@ -6,12 +6,12 @@ { @outer { @middle { @inner - print "inner"; + print ("inner"); break @middle; }; - print "middle"; + print ("middle"); }; - print "outer"; + print ("outer"); }; //expect:inner @@ -20,12 +20,12 @@ { @outer { @middle { @inner - print "inner"; + print ("inner"); break @inner; }; - print "middle"; + print ("middle"); }; - print "outer"; + print ("outer"); }; // nothing to expect here @@ -35,11 +35,11 @@ { break @outer; }; - print "inner"; + print ("inner"); }; - print "middle"; + print ("middle"); }; - print "outer"; + print ("outer"); }; //expect:5.0 @@ -55,7 +55,7 @@ var f = funct() { :result = 10; }; -print f(); +print (f()); //expect:15.0 @@ -63,7 +63,7 @@ f = funct(m, n) :result = m + n ; -print f(f(5, 5), 5); +print (f(f(5, 5), 5)); //expect:10.0 @@ -78,7 +78,7 @@ var g = funct() } ; -print g(); +print (g()); //expect:9.0 @@ -94,4 +94,4 @@ var h = funct() } ; -print h(); \ No newline at end of file +print (h()); \ No newline at end of file diff --git a/tests/closures.nds b/tests/closures.nds index c2d7872..d832829 100644 --- a/tests/closures.nds +++ b/tests/closures.nds @@ -5,18 +5,18 @@ var f1 = funct() { var y = 5; :result = funct() { var z = 8; - print x; + print (x); x = x + 1; :result = funct() { - print x; + print (x); x = x + 1; :result = funct() { - print x; - print y; - print z; + print (x); + print (y); + print (z); x = x + 1; :result = funct() { - print x; + print (x); x = x + 1; }; }; @@ -36,7 +36,7 @@ f1()()()()(); var f = funct() { var y = 5; var x = @[ - funct() print y, + funct() print (y), funct() y = y + 1 ]; :result = x; @@ -56,7 +56,7 @@ var f2 = funct() { var x = { @a @b // this captures the internal value, not whatever it returns is assigned to :result = @{ - "get" = funct() print :a, + "get" = funct() print (:a), "set" = funct(n) :b = n, }; }; @@ -73,7 +73,7 @@ inst2["get"](); // capturing args var argcap = funct(n) - :result = funct() print n + :result = funct() print (n) ; //expect:8.1 @@ -98,19 +98,19 @@ var horse2 = newAnimal("horse", "white"); var turtle = newAnimal("turtle", "brown"); //expect:horse -print horse1["getSpecies"](); +print (horse1["getSpecies"]()); horse1["setSpecies"]("zebra"); //expect:zebra -print horse1["getSpecies"](); +print (horse1["getSpecies"]()); //expect:brown -print horse1["getColor"](); +print (horse1["getColor"]()); //expect:horse -print horse2["getSpecies"](); +print (horse2["getSpecies"]()); //expect:white -print horse2["getColor"](); +print (horse2["getColor"]()); //expect:turtle -print turtle["getSpecies"](); +print (turtle["getSpecies"]()); // closure examples from craftinginterpreters @@ -118,7 +118,7 @@ print turtle["getSpecies"](); var makeClosure = funct(value) { var closure = funct() { - print value; + print (value); }; :result = closure; }; @@ -136,14 +136,14 @@ var outer = funct() { var x = "value"; var middle = funct() { var inner = funct() { - print x; + print (x); }; - print "create inner closure"; + print ("create inner closure"); :result = inner; }; - print "return from outer"; + print ("return from outer"); :result = middle; }; @@ -165,7 +165,7 @@ outer = funct() { var c = 3; var d = 4; var inner = funct() { - print a + c + b + d; + print (a + c + b + d); }; result = inner; }; @@ -184,7 +184,7 @@ outer = funct() { x = "assigned"; }; inner(); - print x; + print (x); }; //expect:assigned @@ -199,7 +199,7 @@ var main5 = funct() { var a = "initial"; var set = funct() { a = "updated"; }; - var get = funct() { print a; }; + var get = funct() { print (a); }; globalSet = set; globalGet = get; @@ -217,15 +217,15 @@ globalGet(); { var a = 1; var f = funct() { - print a; + print (a); }; var b = 2; var g = funct() { - print b; + print (b); }; var c = 3; var h = funct() { - print c; + print (c); }; f(); @@ -241,15 +241,15 @@ globalGet(); var bonus = funct() { var a = 1; var f = funct() { - print a; + print (a); }; var b = 2; var g = funct() { - print b; + print (b); }; var c = 3; var h = funct() { - print c; + print (c); }; :result = @[f, g, h]; diff --git a/tests/collections.nds b/tests/collections.nds index 2bca596..1b1c1b1 100644 --- a/tests/collections.nds +++ b/tests/collections.nds @@ -5,5 +5,5 @@ var returnlist = funct() { }; //expect:3.0 -print returnlist()[2]; +print (returnlist()[2]); diff --git a/tests/controlflow.nds b/tests/controlflow.nds index a6ae279..0055350 100644 --- a/tests/controlflow.nds +++ b/tests/controlflow.nds @@ -4,40 +4,40 @@ //expect:true if (true) { - print "true"; + print ("true"); }; if (false) { - print "false"; + print ("false"); }; // return the condition if falsy //expect:nil -print if (nil) 5; +print (if (nil) 5); //expect:false -print if (false) 5; +print (if (false) 5); // return body if truthy //expect:5.0 -print if (true) 5; +print (if (true) 5); // return else body if falsey and present //expect:6.0 -print if (false) 5 else 6; +print (if (false) 5 else 6); // but still only return the if body if truthy //expect:4.0 -print if (true) 4 else 6; +print (if (true) 4 else 6); // elseif chains //expect:4.0 -print if (false) 1 else if (false) 2 else if (false) 3 else if (true) 4 else if (false) 5 else 8; +print (if (false) 1 else if (false) 2 else if (false) 3 else if (true) 4 else if (false) 5 else 8); // falsiness, truthiness @@ -45,22 +45,22 @@ print if (false) 1 else if (false) 2 else if (false) 3 else if (true) 4 else if var uninitialized; -if (false) print "don't see this"; -if (nil) print "don't see this"; -if (uninitialized) print "don't see this"; +if (false) print ("don't see this"); +if (nil) print ("don't see this"); +if (uninitialized) print ("don't see this"); // the rest of the types are truthy -if (true) print "1"; -if ("") print "2"; -if ("hello") print "3"; -if (0) print "4"; -if (1) print "5"; -if (@[]) print "6"; -if (@["hi"]) print "7"; -if (@{}) print "8"; -if (@{"hi" = 5}) print "9"; -if (funct(n) print n) print "10"; +if (true) print ("1"); +if ("") print ("2"); +if ("hello") print ("3"); +if (0) print ("4"); +if (1) print ("5"); +if (@[]) print ("6"); +if (@["hi"]) print ("7"); +if (@{}) print ("8"); +if (@{"hi" = 5}) print ("9"); +if (funct(n) print (n)) print ("10"); //expect:1 //expect:2 @@ -78,18 +78,18 @@ if (funct(n) print n) print "10"; // and returns the left one if it's falsey or the right one if the left one is truthy //expect:false -print false and 5; +print (false and 5); //expect:5.0 -print true and 5; +print (true and 5); // or returns the leftmost truthy //expect:5.0 -print false or false or false or false or 5 or false or 7; +print (false or false or false or false or 5 or false or 7); //expect:true -print true or false; +print (true or false); // while @@ -101,12 +101,12 @@ while (i < 300) { i = i + 1; }; -print i; //expect:300.0 +print (i); //expect:300.0 i = 5; while (i > 0) - print i = i - 1 + print (i = i - 1) ; //expect:4.0 //expect:3.0 @@ -122,7 +122,7 @@ var res = while (i > 0) ; //expect:0.0 -print res; +print (res); // if no iterations are done, it returns nil @@ -131,4 +131,4 @@ res = while (false) ; //expect:nil -print res; +print (res); diff --git a/tests/precedence.nds b/tests/precedence.nds index 158041e..b1b9932 100644 --- a/tests/precedence.nds +++ b/tests/precedence.nds @@ -3,13 +3,13 @@ // groupings //expect:15.0 -print 5 * (1 + 2); +print (5 * (1 + 2)); //expect:11.0 -print (5 * 2) + 1; +print ((5 * 2) + 1); //expect:-9.0 -print -((3 + 2) * 2) + 1; +print (-((3 + 2) * 2) + 1); // calls @@ -24,7 +24,7 @@ var returnlist = funct() { }; //expect:5.0 -print returnlist()[2]; +print (returnlist()[2]); // priority over unary var truesayer = funct() { @@ -32,4 +32,4 @@ var truesayer = funct() { }; //expect:false -print !truesayer(); \ No newline at end of file +print (!truesayer()); \ No newline at end of file diff --git a/tests/shadowing.nds b/tests/shadowing.nds index 06d3ba0..1d360b7 100644 --- a/tests/shadowing.nds +++ b/tests/shadowing.nds @@ -3,11 +3,11 @@ var a = 5; var a = 3; { var a = 2; - print a; + print (a); //expect:2.0 }; - print a; + print (a); //expect:3.0 }; -print a; +print (a); //expect:5.0 \ No newline at end of file