sugars: :: and table . op; allow for reading existing locals of the same name in var statements
This commit is contained in:
parent
2517439caf
commit
8262ca187c
|
@ -9,6 +9,7 @@ type
|
||||||
opClosure, # closures
|
opClosure, # closures
|
||||||
opPop, opPopSA, opPopA # pop
|
opPop, opPopSA, opPopA # pop
|
||||||
opNegate, opNot # unary
|
opNegate, opNot # unary
|
||||||
|
opSwap, # stack manipulation
|
||||||
opAdd, opSubtract, opMultiply, opDivide, # math
|
opAdd, opSubtract, opMultiply, opDivide, # math
|
||||||
opEqual, opGreater, opLess, # comparison
|
opEqual, opGreater, opLess, # comparison
|
||||||
opTrue, opFalse, opNil, # literal
|
opTrue, opFalse, opNil, # literal
|
||||||
|
@ -91,6 +92,7 @@ proc writeConstant*(ch: var Chunk, constant: NdValue, line: int): int =
|
||||||
const simpleInstructions = {
|
const simpleInstructions = {
|
||||||
opReturn,
|
opReturn,
|
||||||
opPop,
|
opPop,
|
||||||
|
opSwap,
|
||||||
opNegate, opNot,
|
opNegate, opNot,
|
||||||
opAdd, opSubtract, opMultiply, opDivide,
|
opAdd, opSubtract, opMultiply, opDivide,
|
||||||
opEqual, opGreater, opLess,
|
opEqual, opGreater, opLess,
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import ../scanner
|
import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
|
import ../types/value
|
||||||
|
import bitops
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
@ -68,3 +68,17 @@ proc parseIndex(comp: Compiler) =
|
||||||
comp.writeChunk(-1, opGetIndex)
|
comp.writeChunk(-1, opGetIndex)
|
||||||
|
|
||||||
tkLeftBracket.genRule(nop, parseIndex, pcIndex)
|
tkLeftBracket.genRule(nop, parseIndex, pcIndex)
|
||||||
|
|
||||||
|
proc parseDotIndex(comp: Compiler) =
|
||||||
|
# the index
|
||||||
|
comp.consume(tkIdentifier, "Identifier expected after dot index.")
|
||||||
|
let index = comp.previous.text.fromNimString()
|
||||||
|
comp.writeConstant(index)
|
||||||
|
|
||||||
|
if comp.match(tkEqual):
|
||||||
|
comp.parsePrecedence(pcNonAssignTop)
|
||||||
|
comp.writeChunk(-2, opSetIndex)
|
||||||
|
else:
|
||||||
|
comp.writeChunk(-1, opGetIndex)
|
||||||
|
|
||||||
|
tkDot.genRule(nop, parseDotIndex, pcIndex)
|
|
@ -3,8 +3,6 @@
|
||||||
import ../scanner
|
import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
@ -18,7 +16,8 @@ proc ifExpr(comp: Compiler) =
|
||||||
comp.expression()
|
comp.expression()
|
||||||
comp.consume(tkRightParen, "Expect ')' after condition.")
|
comp.consume(tkRightParen, "Expect ')' after condition.")
|
||||||
|
|
||||||
let thenJump = comp.emitJump(0, opJumpIfFalse)
|
var thenStacklen = 0
|
||||||
|
let thenJump = comp.emitJump(0, opJumpIfFalse, thenStacklen)
|
||||||
|
|
||||||
# conditional code that can be jumped over must leave the stack in tact!
|
# conditional code that can be jumped over must leave the stack in tact!
|
||||||
|
|
||||||
|
@ -26,40 +25,44 @@ proc ifExpr(comp: Compiler) =
|
||||||
comp.expression()
|
comp.expression()
|
||||||
# net change to stack: -1 + 1 = 0
|
# net change to stack: -1 + 1 = 0
|
||||||
|
|
||||||
let elseJump = comp.emitJump(0, opJump)
|
var elseStacklen = 0
|
||||||
comp.patchJump(thenJump)
|
let elseJump = comp.emitJump(0, opJump, elseStacklen)
|
||||||
|
comp.patchJump(thenJump, thenStacklen)
|
||||||
|
|
||||||
if comp.match(tkElse):
|
if comp.match(tkElse):
|
||||||
comp.writeChunk(-1, opPop)
|
comp.writeChunk(-1, opPop)
|
||||||
comp.expression()
|
comp.expression()
|
||||||
|
|
||||||
comp.patchJump(elseJump)
|
comp.patchJump(elseJump, elseStacklen)
|
||||||
|
|
||||||
|
|
||||||
tkIf.genRule(ifExpr, nop, pcNone)
|
tkIf.genRule(ifExpr, nop, pcNone)
|
||||||
|
|
||||||
proc andExpr(comp: Compiler) =
|
proc andExpr(comp: Compiler) =
|
||||||
let endJump = comp.emitJump(0, opJumpIfFalse)
|
var stacklen = 0
|
||||||
|
let endJump = comp.emitJump(0, opJumpIfFalse, stacklen)
|
||||||
|
|
||||||
comp.writeChunk(-1, opPop)
|
comp.writeChunk(-1, opPop)
|
||||||
comp.parsePrecedence(pcAnd)
|
comp.parsePrecedence(pcAnd)
|
||||||
# net effect on stack: -1 + 1 = 0
|
# net effect on stack: -1 + 1 = 0
|
||||||
|
|
||||||
comp.patchJump(endJump)
|
comp.patchJump(endJump, stacklen)
|
||||||
|
|
||||||
tkAnd.genRule(nop, andExpr, pcAnd)
|
tkAnd.genRule(nop, andExpr, pcAnd)
|
||||||
|
|
||||||
proc orExpr(comp: Compiler) =
|
proc orExpr(comp: Compiler) =
|
||||||
let elseJump = comp.emitJump(0, opJumpIfFalse)
|
var elseStacklen = 0
|
||||||
let endJump = comp.emitJump(0, opJump)
|
var endStacklen = 0
|
||||||
|
let elseJump = comp.emitJump(0, opJumpIfFalse, elseStacklen)
|
||||||
|
let endJump = comp.emitJump(0, opJump, endStacklen)
|
||||||
|
|
||||||
comp.patchJump(elseJump)
|
comp.patchJump(elseJump, elseStacklen)
|
||||||
|
|
||||||
comp.writeChunk(-1, opPop)
|
comp.writeChunk(-1, opPop)
|
||||||
comp.parsePrecedence(pcOr)
|
comp.parsePrecedence(pcOr)
|
||||||
# net effect on stack: -1 + 1 = 0
|
# net effect on stack: -1 + 1 = 0
|
||||||
|
|
||||||
comp.patchJump(endJump)
|
comp.patchJump(endJump, endStacklen)
|
||||||
|
|
||||||
tkOr.genRule(nop, orExpr, pcOr)
|
tkOr.genRule(nop, orExpr, pcOr)
|
||||||
|
|
||||||
|
@ -67,12 +70,14 @@ proc parseWhile(comp: Compiler) =
|
||||||
|
|
||||||
comp.writeChunk(1, opNil) # return value
|
comp.writeChunk(1, opNil) # return value
|
||||||
let loopStart = comp.chunk.len
|
let loopStart = comp.chunk.len
|
||||||
|
let loopStacklen = comp.stackIndex
|
||||||
comp.consume(tkLeftParen, "Expect '(' after 'while'.")
|
comp.consume(tkLeftParen, "Expect '(' after 'while'.")
|
||||||
# condition
|
# condition
|
||||||
comp.expression()
|
comp.expression()
|
||||||
comp.consume(tkRightParen, "Expect ')' after condition.")
|
comp.consume(tkRightParen, "Expect ')' after condition.")
|
||||||
|
|
||||||
let exitJump = comp.emitJump(-1, opJumpIfFalsePop)
|
var exitStacklen = 0
|
||||||
|
let exitJump = comp.emitJump(-1, opJumpIfFalsePop, exitStacklen)
|
||||||
# this cannot be handled with just opPop, since the net change in the
|
# this cannot be handled with just opPop, since the net change in the
|
||||||
# stack size inside code that is conditional must be 0!
|
# stack size inside code that is conditional must be 0!
|
||||||
|
|
||||||
|
@ -81,7 +86,7 @@ proc parseWhile(comp: Compiler) =
|
||||||
comp.expression()
|
comp.expression()
|
||||||
# net stack change: 1 + -1 = 0
|
# net stack change: 1 + -1 = 0
|
||||||
|
|
||||||
comp.emitLoop(loopstart = loopStart, delta = 0, op = opLoop)
|
comp.emitLoop(loopstart = loopStart, delta = 0, op = opLoop, loopStacklen)
|
||||||
comp.patchJump(exitJump)
|
comp.patchJump(exitJump, exitStacklen)
|
||||||
|
|
||||||
tkWhile.genRule(parseWhile, nop, pcNone)
|
tkWhile.genRule(parseWhile, nop, pcNone)
|
||||||
|
|
|
@ -8,8 +8,6 @@ import ../chunk
|
||||||
import ../types/value
|
import ../types/value
|
||||||
import ../config
|
import ../config
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
|
|
@ -6,8 +6,6 @@ import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
import ../config
|
import ../config
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
@ -19,6 +17,18 @@ proc grouping(comp: Compiler) =
|
||||||
comp.expression()
|
comp.expression()
|
||||||
comp.consume(tkRightParen, "Expect ')' after expression.")
|
comp.consume(tkRightParen, "Expect ')' after expression.")
|
||||||
|
|
||||||
|
proc parseArgs(comp: Compiler): int =
|
||||||
|
var argcount = 0
|
||||||
|
# put args on stack
|
||||||
|
while comp.current.tokenType notin {tkRightParen, tkEof}:
|
||||||
|
comp.expression()
|
||||||
|
inc argcount
|
||||||
|
if comp.current.tokenType != tkRightParen:
|
||||||
|
comp.consume(tkComma, "Expected ',' between arguments in function calls.")
|
||||||
|
comp.consume(tkRightParen, "Expected ')' after arguments in function calls.")
|
||||||
|
return argcount
|
||||||
|
|
||||||
|
|
||||||
proc parseCall(comp: Compiler) =
|
proc parseCall(comp: Compiler) =
|
||||||
# ( consumed
|
# ( consumed
|
||||||
|
|
||||||
|
@ -28,14 +38,7 @@ proc parseCall(comp: Compiler) =
|
||||||
# opCall converts it to this
|
# opCall converts it to this
|
||||||
# ... <ret val> <arg1> <arg2> <arg3>
|
# ... <ret val> <arg1> <arg2> <arg3>
|
||||||
|
|
||||||
var argcount = 0
|
let argcount = comp.parseArgs()
|
||||||
# put args on stack
|
|
||||||
while comp.current.tokenType notin {tkRightParen, tkEof}:
|
|
||||||
comp.expression()
|
|
||||||
inc argcount
|
|
||||||
if comp.current.tokenType != tkRightParen:
|
|
||||||
comp.consume(tkComma, "Expected ',' between arguments in function calls.")
|
|
||||||
comp.consume(tkRightParen, "Expected ')' after arguments in function calls.")
|
|
||||||
|
|
||||||
# emit call
|
# emit call
|
||||||
comp.writeChunk(-argcount, opCall)
|
comp.writeChunk(-argcount, opCall)
|
||||||
|
@ -43,10 +46,32 @@ proc parseCall(comp: Compiler) =
|
||||||
|
|
||||||
tkLeftParen.genRule(grouping, parseCall, pcCall)
|
tkLeftParen.genRule(grouping, parseCall, pcCall)
|
||||||
|
|
||||||
|
proc parsePipeCall(comp: Compiler) =
|
||||||
|
# can be followed by:
|
||||||
|
# idents
|
||||||
|
# idents+indexes
|
||||||
|
# NOT calls, so the parsePrecedence after it should look for pcIndex at most
|
||||||
|
|
||||||
|
# get the function on the stack
|
||||||
|
comp.parsePrecedence(pcIndex)
|
||||||
|
|
||||||
|
# swap the function and the first arg
|
||||||
|
comp.writeChunk(0, opSwap)
|
||||||
|
|
||||||
|
var argcount = 1
|
||||||
|
if comp.match(tkLeftParen):
|
||||||
|
argcount = 1 + comp.parseArgs()
|
||||||
|
|
||||||
|
comp.writeChunk(-argcount, opCall)
|
||||||
|
comp.writeChunk(0, argcount.uint8)
|
||||||
|
|
||||||
|
tkDoublecolon.genRule(nop, parsePipeCall, pcAmpersand)
|
||||||
|
|
||||||
proc parseFunct(comp: Compiler) =
|
proc parseFunct(comp: Compiler) =
|
||||||
|
|
||||||
# jump over
|
# jump over
|
||||||
let jumpOverBody = comp.emitJump(1, opFunctionDef)
|
var jumpStacklen = 0
|
||||||
|
let jumpOverBody = comp.emitJump(1, opFunctionDef, jumpStacklen)
|
||||||
|
|
||||||
comp.consume(tkLeftParen, "Expected '(' after keyword 'funct'.")
|
comp.consume(tkLeftParen, "Expected '(' after keyword 'funct'.")
|
||||||
|
|
||||||
|
@ -89,7 +114,7 @@ proc parseFunct(comp: Compiler) =
|
||||||
dec comp.stackIndex # the previous end scope did not put anything on the stack, it is jumped over
|
dec comp.stackIndex # the previous end scope did not put anything on the stack, it is jumped over
|
||||||
|
|
||||||
# end of function declaration:
|
# end of function declaration:
|
||||||
comp.patchJump(jumpOverBody)
|
comp.patchJump(jumpOverBody, jumpStacklen)
|
||||||
# closures are implemented in a way, where the vm pops the function from the stack
|
# closures are implemented in a way, where the vm pops the function from the stack
|
||||||
# and reads the upvalue details from the following bytes
|
# and reads the upvalue details from the following bytes
|
||||||
if f.upvalues.len() > 0:
|
if f.upvalues.len() > 0:
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
import ../chunk
|
import ../chunk
|
||||||
|
import ../config
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
# JUMP HELPERS
|
# JUMP HELPERS
|
||||||
|
|
||||||
proc emitJump*(comp: Compiler, delta: int, op: OpCode): int =
|
proc emitJump*(comp: Compiler, delta: int, op: OpCode, stacklen: var int): int =
|
||||||
# delta -> 0 if the jump does not pop
|
# delta -> 0 if the jump does not pop
|
||||||
# delta -> -1 if the jump pops the condition from the stack
|
# delta -> -1 if the jump pops the condition from the stack
|
||||||
comp.writeChunk(delta, op)
|
comp.writeChunk(delta, op)
|
||||||
comp.writeChunk(0, 0xffffff.toDU8)
|
comp.writeChunk(0, 0xffffff.toDU8)
|
||||||
|
when assertionsCompiler:
|
||||||
|
stacklen = comp.stackIndex
|
||||||
comp.chunk.len - argSize
|
comp.chunk.len - argSize
|
||||||
|
|
||||||
proc patchJump*(comp: Compiler, offset: int) =
|
proc patchJump*(comp: Compiler, offset: int, stacklen: int) =
|
||||||
|
when assertionsCompiler:
|
||||||
|
if comp.stackIndex != stacklen:
|
||||||
|
comp.error("Assertion failed: loop doesn't preserve stackindex.")
|
||||||
let jump = (comp.chunk.len - offset - argSize)
|
let jump = (comp.chunk.len - offset - argSize)
|
||||||
|
|
||||||
if (jump > argMax):
|
if (jump > argMax):
|
||||||
|
@ -25,7 +29,10 @@ proc patchJump*(comp: Compiler, offset: int) =
|
||||||
comp.chunk.code[offset] = jumpt[0]
|
comp.chunk.code[offset] = jumpt[0]
|
||||||
comp.chunk.code[offset + 1] = jumpt[1]
|
comp.chunk.code[offset + 1] = jumpt[1]
|
||||||
|
|
||||||
proc emitLoop*(comp: Compiler, loopstart: int, delta: int, op: OpCode) =
|
proc emitLoop*(comp: Compiler, loopstart: int, delta: int, op: OpCode, stacklen: int) =
|
||||||
|
when assertionsCompiler:
|
||||||
|
if comp.stackIndex != stacklen:
|
||||||
|
comp.error("Assertion failed: loop doesn't preserve stackindex.")
|
||||||
comp.writeChunk(delta, op)
|
comp.writeChunk(delta, op)
|
||||||
|
|
||||||
let offset = comp.chunk.len - loopstart + argSize
|
let offset = comp.chunk.len - loopstart + argSize
|
||||||
|
|
|
@ -6,8 +6,6 @@ import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
import ../config
|
import ../config
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import jumps
|
import jumps
|
||||||
|
@ -65,7 +63,8 @@ proc jumpToEnd*(comp: Compiler, scope: Scope) =
|
||||||
else:
|
else:
|
||||||
delta = comp.stackIndex - scope.goalStackIndex
|
delta = comp.stackIndex - scope.goalStackIndex
|
||||||
comp.writePops(delta)
|
comp.writePops(delta)
|
||||||
let jmp = comp.emitJump(delta, opJump)
|
var s = 0 # discard the saved stack length
|
||||||
|
let jmp = comp.emitJump(delta, opJump, s)
|
||||||
scope.jumps.add(jmp)
|
scope.jumps.add(jmp)
|
||||||
|
|
||||||
proc endScope*(comp: Compiler): Scope =
|
proc endScope*(comp: Compiler): Scope =
|
||||||
|
@ -94,7 +93,7 @@ proc endScope*(comp: Compiler): Scope =
|
||||||
comp.restore(popped)
|
comp.restore(popped)
|
||||||
# patch jumps to after the scope (such jumps from breaks emit the pops before jumping)
|
# patch jumps to after the scope (such jumps from breaks emit the pops before jumping)
|
||||||
for jump in popped.jumps:
|
for jump in popped.jumps:
|
||||||
comp.patchJump(jump)
|
comp.patchJump(jump, popped.goalStackIndex)
|
||||||
if function:
|
if function:
|
||||||
comp.writeChunk(0, opReturn)
|
comp.writeChunk(0, opReturn)
|
||||||
# remove locals from the comp object that were of this scope
|
# remove locals from the comp object that were of this scope
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
import ../scanner
|
import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
|
|
@ -6,8 +6,6 @@ import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
import ../types/value
|
import ../types/value
|
||||||
|
|
||||||
# the following order of imports here is important
|
|
||||||
# it defines the allowed dependency precedence between the compiler files
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
import precedence
|
import precedence
|
||||||
|
@ -60,9 +58,10 @@ proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool
|
||||||
let cfunc: Scope = if comp.scopes.len() > 0: comp.scopes[comp.scopes.high()].parentFunction else: nil
|
let cfunc: Scope = if comp.scopes.len() > 0: comp.scopes[comp.scopes.high()].parentFunction else: nil
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
let local = comp.locals[i]
|
let local = comp.locals[i]
|
||||||
|
i.dec
|
||||||
if local.name == name:
|
if local.name == name:
|
||||||
if local.depth == -1:
|
if local.depth == -1:
|
||||||
comp.error("Can't read local variable in its own initializer.")
|
continue
|
||||||
let upvalue = local.scope.parentFunction != cfunc
|
let upvalue = local.scope.parentFunction != cfunc
|
||||||
if not upvalue:
|
if not upvalue:
|
||||||
return (local.index, false)
|
return (local.index, false)
|
||||||
|
@ -70,7 +69,6 @@ proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool
|
||||||
# resolveUpvalue
|
# resolveUpvalue
|
||||||
local.captured = true
|
local.captured = true
|
||||||
return (comp.addUpvalue(local), true)
|
return (comp.addUpvalue(local), true)
|
||||||
i.dec
|
|
||||||
return (index: -1, upvalue: false)
|
return (index: -1, upvalue: false)
|
||||||
|
|
||||||
# EXPRESSIONS
|
# EXPRESSIONS
|
||||||
|
|
|
@ -11,7 +11,7 @@ type
|
||||||
|
|
||||||
TokenType* = enum
|
TokenType* = enum
|
||||||
tkNone, # the default tokentype, if encountered anywhere, erroring out is the best course of action
|
tkNone, # the default tokentype, if encountered anywhere, erroring out is the best course of action
|
||||||
tkLeftParen, tkRightParen, tkLeftBrace, tkRightBrace, tkComma, tkDot,
|
tkLeftParen, tkRightParen, tkLeftBrace, tkRightBrace, tkComma, tkDot, tkColon, tkDoublecolon,
|
||||||
tkMinus, tkPlus, tkSemicolon, tkSlash, tkStar, tkBang, tkBangEqual,
|
tkMinus, tkPlus, tkSemicolon, tkSlash, tkStar, tkBang, tkBangEqual,
|
||||||
tkGreater, tkGreaterEqual, tkLess, tkLessEqual, tkEqual, tkEqualEqual,
|
tkGreater, tkGreaterEqual, tkLess, tkLessEqual, tkEqual, tkEqualEqual,
|
||||||
tkStartList, tkStartTable, tkLeftBracket, tkRightBracket,
|
tkStartList, tkStartTable, tkLeftBracket, tkRightBracket,
|
||||||
|
@ -198,7 +198,12 @@ proc scanToken*(scanner: Scanner): Token =
|
||||||
elif scanner.match('{'): return scanner.makeToken(tkStartTable)
|
elif scanner.match('{'): return scanner.makeToken(tkStartTable)
|
||||||
else: return scanner.scanLabel()
|
else: return scanner.scanLabel()
|
||||||
else:
|
else:
|
||||||
|
if c == ':' and scanner.match(':'):
|
||||||
|
return scanner.makeToken(tkDoublecolon)
|
||||||
if c.canStartIdent():
|
if c.canStartIdent():
|
||||||
return scanner.scanIdentifier()
|
# : can start ident, but if on its own it's probably syntactic sugar for tables
|
||||||
|
if c == ':' and not scanner.peek().canContIdent():
|
||||||
|
return scanner.makeToken(tkColon)
|
||||||
|
return scanner.scanIdentifier()
|
||||||
else:
|
else:
|
||||||
return scanner.errorToken("Unexpected character.")
|
return scanner.errorToken("Unexpected character.")
|
||||||
|
|
|
@ -72,6 +72,15 @@ proc settip*[T](stack: var Stack[T], newtip: T) =
|
||||||
raise newException(Defect, "Stacktop is nil or smaller than start")
|
raise newException(Defect, "Stacktop is nil or smaller than start")
|
||||||
stack.top[]= newtip
|
stack.top[]= newtip
|
||||||
|
|
||||||
|
proc swap*[T](stack: var Stack[T]) =
|
||||||
|
when boundsChecks:
|
||||||
|
if stack.top == nil or stack.len() < 2:
|
||||||
|
raise newException(Defect, "Stacktop is nil, or not high enough for swap.")
|
||||||
|
let temp = stack.top[]
|
||||||
|
var below = stack.top.psub(sizeof(T))
|
||||||
|
stack.top[] = below[]
|
||||||
|
below[] = temp
|
||||||
|
|
||||||
proc deleteTopN*[T](stack: var Stack[T], n: Natural) =
|
proc deleteTopN*[T](stack: var Stack[T], n: Natural) =
|
||||||
stack.top = stack.top.psub(sizeof(T) * n)
|
stack.top = stack.top.psub(sizeof(T) * n)
|
||||||
when boundsChecks:
|
when boundsChecks:
|
||||||
|
|
|
@ -186,6 +186,8 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
of opPopSA:
|
of opPopSA:
|
||||||
let amt = ip.readUI8()
|
let amt = ip.readUI8()
|
||||||
stack.popn(amt)
|
stack.popn(amt)
|
||||||
|
of opSwap:
|
||||||
|
stack.swap()
|
||||||
of opConstant:
|
of opConstant:
|
||||||
let val: NdValue = ip.readConstant(chunk)
|
let val: NdValue = ip.readConstant(chunk)
|
||||||
stack.add(val)
|
stack.add(val)
|
||||||
|
|
|
@ -82,8 +82,8 @@ argcap(8.1)();
|
||||||
|
|
||||||
// oop: constructors, getters, setters
|
// oop: constructors, getters, setters
|
||||||
|
|
||||||
var newAnimal = funct(pspecies, color) {
|
var newAnimal = funct(species, color) {
|
||||||
var species = pspecies; // copy it so that it's mutable, if args ever get immutable
|
var species = species; // copy it so that it's mutable, if args ever get immutable
|
||||||
var animal = @{
|
var animal = @{
|
||||||
"getSpecies" = funct() :result = species,
|
"getSpecies" = funct() :result = species,
|
||||||
"setSpecies" = funct(newSpecies) species = newSpecies,
|
"setSpecies" = funct(newSpecies) species = newSpecies,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
print (0/0 == 0/0);
|
||||||
|
//expect:false
|
||||||
|
|
||||||
|
print (1/0 == 3/0);
|
||||||
|
//expect:true
|
||||||
|
|
||||||
|
print (1/0 == -2/0);
|
||||||
|
//expect:false
|
|
@ -0,0 +1,43 @@
|
||||||
|
// testing syntactic sugars
|
||||||
|
|
||||||
|
// :: piping function call
|
||||||
|
|
||||||
|
var double = funct(num) :result = num * 2;
|
||||||
|
|
||||||
|
var four = 2 :: double();
|
||||||
|
|
||||||
|
var multiply = funct(num, factor) :result = num * factor;
|
||||||
|
|
||||||
|
var six = 2 :: multiply(3);
|
||||||
|
|
||||||
|
print (four);
|
||||||
|
print (six);
|
||||||
|
//expect:4.0
|
||||||
|
//expect:6.0
|
||||||
|
|
||||||
|
print (1 + 2 * 3 :: tostring() + "2");
|
||||||
|
//expect:7.02
|
||||||
|
|
||||||
|
// indexing tables with .
|
||||||
|
|
||||||
|
var nested = @{
|
||||||
|
"lvl1" = @{
|
||||||
|
"lvl2" = @{
|
||||||
|
"lvl3" = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
nested.lvl1.lvl2.lvl3 = 6;
|
||||||
|
print(nested.lvl1.lvl2.lvl3);
|
||||||
|
//expect:6.0
|
||||||
|
|
||||||
|
nested["lvl1"].lvl2["lvl3"] = 2;
|
||||||
|
print(nested.lvl1["lvl2"].lvl3);
|
||||||
|
//expect:2.0
|
||||||
|
|
||||||
|
nested["lvl1"]["lvl2"]["lvl3"] = -5.4;
|
||||||
|
nested .lvl1 .lvl2 .lvl3 :: print;
|
||||||
|
//expect:-5.4
|
||||||
|
|
||||||
|
// creating tables with : and no [] idents
|
Loading…
Reference in New Issue