Minor bug fixes to chunks, function calls are still broken

This commit is contained in:
nocturn9x 2020-09-03 19:24:18 +02:00
parent 0fd7cea1e1
commit cb384aaf5f
9 changed files with 268 additions and 230 deletions

View File

@ -4,6 +4,7 @@
import tables
import strformat
import strutils
import meta/valueobject
import meta/tokenobject
import meta/looptype
@ -23,10 +24,10 @@ type
lastPop*: Value
frameCount*: int
source*: string
frames*: seq[CallFrame]
stack*: seq[Value]
frames*: ref seq[CallFrame]
stack*: ref seq[Value]
stackTop*: int
objects*: ptr Obj
objects*: seq[ptr Obj]
globals*: Table[string, Value]
file*: string
@ -35,7 +36,7 @@ type
depth*: int
Compiler* = object
enclosing*: ptr Compiler
enclosing*: ref Compiler
function*: ptr Function
context*: FunctionType
locals*: seq[Local]
@ -43,7 +44,7 @@ type
scopeDepth*: int
parser*: Parser
loop*: Loop
vm*: VM
vm*: ptr VM
file*: string
Parser* = ref object
@ -71,11 +72,11 @@ func stringify*(value: Value): string =
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = cast[ptr String](value.obj)[].stringify
result = cast[ptr String](value.obj).stringify
of ObjectTypes.FUNCTION:
result = cast[ptr Function](value.obj)[].stringify
result = cast[ptr Function](value.obj).stringify
else:
result = value.obj[].stringify()
result = value.obj.stringify()
of ValueTypes.NAN:
result = "nan"
of ValueTypes.INF:
@ -85,7 +86,110 @@ func stringify*(value: Value): string =
proc stringify*(frame: CallFrame): string =
return &"CallFrame(slots={frame.slots}, ip={frame.ip}, function={stringify(frame.function[])})"
return &"CallFrame(slots={frame.slots}, ip={frame.ip}, function={stringify(frame.function)})"
proc hashFloat(f: float): uint32 =
result = 2166136261u32
result = result xor uint32 f
result *= 16777619
return result
proc hash*(value: Value): uint32 =
case value.kind:
of INTEGER:
result = uint32 value.toInt()
of BOOL:
if value.boolValue:
result = uint32 1
else:
result = uint32 0
of DOUBLE:
result = hashFloat(value.toFloat())
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = hash(cast[ptr String](value.obj))
else:
result = hash(value.obj)
else: # More coming soon
result = uint32 0
func isFalsey*(value: Value): bool =
case value.kind:
of BOOL:
result = not value.toBool()
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = cast[ptr String](value.obj).isFalsey()
of ObjectTypes.FUNCTION:
result = cast[ptr Function](value.obj).isFalsey()
else:
result = isFalsey(value.obj)
of INTEGER:
result = value.toInt() == 0
of DOUBLE:
result = value.toFloat() > 0.0
of NIL:
result = true
of ValueTypes.INF, MINF:
result = false
of ValueTypes.NAN:
result = true
func typeName*(value: Value): string =
case value.kind:
of BOOL, NIL, DOUBLE, INTEGER, ValueTypes.NAN, ValueTypes.INF:
result = ($value.kind).toLowerAscii()
of MINF:
result = "inf"
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = cast[ptr String](value.obj).typeName()
of ObjectTypes.FUNCTION:
result = cast[ptr Function](value.obj).typeName()
else:
result = value.obj.typeName()
proc valuesEqual*(a: Value, b: Value): bool =
if a.kind != b.kind:
result = false
else:
case a.kind:
of BOOL:
result = a.toBool() == b.toBool()
of NIL:
result = true
of INTEGER:
result = a.toInt() == b.toInt()
of DOUBLE:
result = a.toFloat() == b.toFloat()
of OBJECT:
case a.obj.kind:
of ObjectTypes.STRING:
var a = cast[ptr String](a.obj)
var b = cast[ptr String](b.obj)
result = valuesEqual(a, b)
of ObjectTypes.FUNCTION:
var a = cast[ptr Function](a.obj)
var b = cast[ptr Function](b.obj)
result = valuesEqual(a, b)
else:
result = valuesEqual(a.obj, b.obj)
of ValueTypes.INF:
result = b.kind == ValueTypes.INF
of MINF:
result = b.kind == MINF
of ValueTypes.NAN:
result = false
proc `$`*(frame: CallFrame): string =
result = stringify(frame)

View File

@ -27,7 +27,7 @@ type
PREC_CALL,
PREC_PRIMARY
ParseFn = proc(self: var Compiler, canAssign: bool): void
ParseFn = proc(self: ref Compiler, canAssign: bool): void
ParseRule = ref object
prefix, infix: ParseFn
@ -78,7 +78,7 @@ proc consume(self: var Parser, expected: TokenType, message: string) =
self.parseError(self.peek(), message)
proc compileError(self: var Compiler, message: string) =
proc compileError(self: ref Compiler, message: string) =
echo &"Traceback (most recent call last):"
echo &" File '{self.file}', line {self.parser.peek.line}, at '{self.parser.peek.lexeme}'"
echo &"CompileError: {message}"
@ -86,29 +86,29 @@ proc compileError(self: var Compiler, message: string) =
self.parser.panicMode = true
proc emitByte(self: var Compiler, byt: OpCode|uint8) =
proc emitByte(self: ref Compiler, byt: OpCode|uint8) =
self.function.chunk.writeChunk(uint8 byt, self.parser.previous.line)
proc emitBytes(self: var Compiler, byt1: OpCode|uint8, byt2: OpCode|uint8) =
proc emitBytes(self: ref Compiler, byt1: OpCode|uint8, byt2: OpCode|uint8) =
self.emitByte(uint8 byt1)
self.emitByte(uint8 byt2)
proc emitBytes(self: var Compiler, bytarr: array[3, uint8]) =
proc emitBytes(self: ref Compiler, bytarr: array[3, uint8]) =
self.emitBytes(bytarr[0], bytarr[1])
self.emitByte(bytarr[2])
proc makeConstant(self: var Compiler, val: Value): uint8 =
proc makeConstant(self: ref Compiler, val: Value): uint8 =
result = uint8 self.function.chunk.addConstant(val)
proc makeLongConstant(self: var Compiler, val: Value): array[3, uint8] =
proc makeLongConstant(self: ref Compiler, val: Value): array[3, uint8] =
result = self.function.chunk.writeConstant(val)
proc emitConstant(self: var Compiler, value: Value) =
proc emitConstant(self: ref Compiler, value: Value) =
if self.function.chunk.consts.values.len > 255:
self.emitByte(OP_CONSTANT_LONG)
self.emitBytes(self.makeLongConstant(value))
@ -117,18 +117,18 @@ proc emitConstant(self: var Compiler, value: Value) =
proc getRule(kind: TokenType): ParseRule # Forward declarations
proc statement(self: var Compiler)
proc declaration(self: var Compiler)
proc initCompiler*(vm: var VM, context: FunctionType, enclosing: ptr Compiler = nil, parser: Parser = initParser(@[], ""), file: string): Compiler
proc statement(self: ref Compiler)
proc declaration(self: ref Compiler)
proc initCompiler*(vm: ptr VM, context: FunctionType, enclosing: ref Compiler = nil, parser: Parser = initParser(@[], ""), file: string): ref Compiler
proc endCompiler(self: var Compiler): ptr Function =
proc endCompiler(self: ref Compiler): ptr Function =
self.emitByte(OP_NIL)
self.emitByte(OP_RETURN)
return self.function
proc parsePrecedence(self: var Compiler, precedence: Precedence) =
proc parsePrecedence(self: ref Compiler, precedence: Precedence) =
discard self.parser.advance()
var prefixRule = getRule(self.parser.previous.kind).prefix
if prefixRule == nil:
@ -149,11 +149,11 @@ proc parsePrecedence(self: var Compiler, precedence: Precedence) =
self.parser.parseError(self.parser.peek, "Invalid assignment target")
proc expression(self: var Compiler) =
proc expression(self: ref Compiler) =
self.parsePrecedence(PREC_ASSIGNMENT)
proc binary(self: var Compiler, canAssign: bool) =
proc binary(self: ref Compiler, canAssign: bool) =
var operator = self.parser.previous.kind
var rule = getRule(operator)
self.parsePrecedence(Precedence((int rule.precedence) + 1))
@ -192,7 +192,7 @@ proc binary(self: var Compiler, canAssign: bool) =
return
proc unary(self: var Compiler, canAssign: bool) =
proc unary(self: ref Compiler, canAssign: bool) =
var operator = self.parser.previous().kind
if self.parser.peek().kind != EOF:
self.parsePrecedence(PREC_UNARY)
@ -208,24 +208,23 @@ proc unary(self: var Compiler, canAssign: bool) =
return
template markObject*(self: var Compiler, obj: untyped): untyped =
obj.next = self.vm.objects
self.vm.objects = obj
template markObject*(self: ref Compiler, obj: untyped): untyped =
self.vm.objects.add(obj)
obj
proc strVal(self: var Compiler, canAssign: bool) =
proc strVal(self: ref Compiler, canAssign: bool) =
var str = self.parser.previous().lexeme
var delimiter = &"{str[0]}"
str = str.unescape(delimiter, delimiter)
self.emitConstant(Value(kind: OBJECT, obj: self.markObject(newString(str))))
proc bracketAssign(self: var Compiler, canAssign: bool) =
proc bracketAssign(self: ref Compiler, canAssign: bool) =
discard # TODO -> Implement this
proc bracket(self: var Compiler, canAssign: bool) =
proc bracket(self: ref Compiler, canAssign: bool) =
if self.parser.peek.kind == COLON:
self.emitByte(OP_NIL)
discard self.parser.advance()
@ -251,7 +250,7 @@ proc bracket(self: var Compiler, canAssign: bool) =
self.parser.consume(TokenType.RS, "Expecting ']' after slice expression")
proc literal(self: var Compiler, canAssign: bool) =
proc literal(self: ref Compiler, canAssign: bool) =
case self.parser.previous().kind:
of TRUE:
self.emitByte(OP_TRUE)
@ -267,12 +266,12 @@ proc literal(self: var Compiler, canAssign: bool) =
discard # Unreachable
proc number(self: var Compiler, canAssign: bool) =
proc number(self: ref Compiler, canAssign: bool) =
var value = self.parser.previous().literal
self.emitConstant(value)
proc grouping(self: var Compiler, canAssign: bool) =
proc grouping(self: ref Compiler, canAssign: bool) =
if self.parser.match(EOF):
self.parser.parseError(self.parser.previous, "Expecting ')'")
elif self.parser.match(RP):
@ -282,7 +281,7 @@ proc grouping(self: var Compiler, canAssign: bool) =
self.parser.consume(RP, "Expecting ')' after parentheszed expression")
proc synchronize(self: var Compiler) =
proc synchronize(self: ref Compiler) =
self.parser.panicMode = false
while self.parser.peek.kind != EOF:
if self.parser.previous().kind == SEMICOLON:
@ -295,28 +294,28 @@ proc synchronize(self: var Compiler) =
discard self.parser.advance()
proc identifierConstant(self: var Compiler, tok: Token): uint8 =
proc identifierConstant(self: ref Compiler, tok: Token): uint8 =
return self.makeConstant(Value(kind: OBJECT, obj: self.markObject(newString(tok.lexeme))))
proc identifierLongConstant(self: var Compiler, tok: Token): array[3, uint8] =
proc identifierLongConstant(self: ref Compiler, tok: Token): array[3, uint8] =
return self.makeLongConstant(Value(kind: OBJECT, obj: self.markObject(newString(tok.lexeme))))
proc addLocal(self: var Compiler, name: Token) =
proc addLocal(self: ref Compiler, name: Token) =
var local = Local(name: name, depth: -1)
inc(self.localCount)
self.locals.add(local)
proc declareVariable(self: var Compiler) =
proc declareVariable(self: ref Compiler) =
if self.scopeDepth == 0:
return
var name = self.parser.previous()
self.addLocal(name)
proc parseVariable(self: var Compiler, message: string): uint8 =
proc parseVariable(self: ref Compiler, message: string): uint8 =
self.parser.consume(ID, message)
self.declareVariable()
if self.scopeDepth > 0:
@ -324,7 +323,7 @@ proc parseVariable(self: var Compiler, message: string): uint8 =
return self.identifierConstant(self.parser.previous)
proc parseLongVariable(self: var Compiler, message: string): array[3, uint8] =
proc parseLongVariable(self: ref Compiler, message: string): array[3, uint8] =
self.parser.consume(ID, message)
self.declareVariable()
if self.scopeDepth > 0:
@ -332,20 +331,20 @@ proc parseLongVariable(self: var Compiler, message: string): array[3, uint8] =
return self.identifierLongConstant(self.parser.previous)
proc markInitialized(self: var Compiler) =
proc markInitialized(self: ref Compiler) =
if self.scopeDepth == 0:
return
self.locals[self.localCount - 1].depth = self.scopeDepth
proc defineVariable(self: var Compiler, idx: uint8) =
proc defineVariable(self: ref Compiler, idx: uint8) =
if self.scopeDepth > 0:
self.markInitialized()
return
self.emitBytes(OP_DEFINE_GLOBAL, idx)
proc defineVariable(self: var Compiler, idx: array[3, uint8]) =
proc defineVariable(self: ref Compiler, idx: array[3, uint8]) =
if self.scopeDepth > 0:
self.markInitialized()
return
@ -353,7 +352,7 @@ proc defineVariable(self: var Compiler, idx: array[3, uint8]) =
self.emitBytes(idx)
proc resolveLocal(self: var Compiler, name: Token): int =
proc resolveLocal(self: ref Compiler, name: Token): int =
var i = self.localCount - 1
for local in reversed(self.locals):
if local.name.lexeme == name.lexeme:
@ -364,7 +363,7 @@ proc resolveLocal(self: var Compiler, name: Token): int =
return -1
proc namedVariable(self: var Compiler, tok: Token, canAssign: bool) =
proc namedVariable(self: ref Compiler, tok: Token, canAssign: bool) =
var arg = self.resolveLocal(tok)
var
get: OpCode
@ -383,7 +382,7 @@ proc namedVariable(self: var Compiler, tok: Token, canAssign: bool) =
self.emitBytes(get, uint8 arg)
proc namedLongVariable(self: var Compiler, tok: Token, canAssign: bool) =
proc namedLongVariable(self: ref Compiler, tok: Token, canAssign: bool) =
var arg = self.resolveLocal(tok)
var casted = cast[array[3, uint8]](arg)
var
@ -406,14 +405,14 @@ proc namedLongVariable(self: var Compiler, tok: Token, canAssign: bool) =
proc variable(self: var Compiler, canAssign: bool) =
proc variable(self: ref Compiler, canAssign: bool) =
if self.locals.len < 255:
self.namedVariable(self.parser.previous(), canAssign)
else:
self.namedLongVariable(self.parser.previous(), canAssign)
proc varDeclaration(self: var Compiler) =
proc varDeclaration(self: ref Compiler) =
var shortName: uint8
var longName: array[3, uint8]
var useShort: bool = true
@ -433,13 +432,13 @@ proc varDeclaration(self: var Compiler) =
self.defineVariable(longName)
proc expressionStatement(self: var Compiler) =
proc expressionStatement(self: ref Compiler) =
self.expression()
self.parser.consume(SEMICOLON, "Missing semicolon after expression")
self.emitByte(OP_POP)
proc deleteVariable(self: var Compiler, canAssign: bool) =
proc deleteVariable(self: ref Compiler, canAssign: bool) =
self.expression()
if self.parser.previous().kind in [NUMBER, STR]:
self.compileError("cannot delete a literal")
@ -459,31 +458,31 @@ proc deleteVariable(self: var Compiler, canAssign: bool) =
self.emitBytes(name[1], name[2])
proc parseBlock(self: var Compiler) =
proc parseBlock(self: ref Compiler) =
while not self.parser.check(RB) and not self.parser.check(EOF):
self.declaration()
self.parser.consume(RB, "Expecting '}' after block statement")
proc beginScope(self: var Compiler) =
proc beginScope(self: ref Compiler) =
inc(self.scopeDepth)
proc endScope(self: var Compiler) =
proc endScope(self: ref Compiler) =
self.scopeDepth = self.scopeDepth - 1
while self.localCount > 0 and self.locals[self.localCount - 1].depth > self.scopeDepth:
self.emitByte(OP_POP)
self.localCount = self.localCount - 1
proc emitJump(self: var Compiler, opcode: OpCode): int =
proc emitJump(self: ref Compiler, opcode: OpCode): int =
self.emitByte(opcode)
self.emitByte(0xff)
self.emitByte(0xff)
return self.function.chunk.code.len - 2
proc patchJump(self: var Compiler, offset: int) =
proc patchJump(self: ref Compiler, offset: int) =
var jump = self.function.chunk.code.len - offset - 2
if jump > (int uint16.high):
self.compileError("too much code to jump over")
@ -492,7 +491,7 @@ proc patchJump(self: var Compiler, offset: int) =
self.function.chunk.code[offset + 1] = uint8 jump and 0xff
proc ifStatement(self: var Compiler) =
proc ifStatement(self: ref Compiler) =
self.parser.consume(LP, "The if condition must be parenthesized")
if self.parser.peek.kind != EOF:
self.expression()
@ -514,7 +513,7 @@ proc ifStatement(self: var Compiler) =
self.parser.parseError(self.parser.previous, "The if condition must be parenthesized")
proc emitLoop(self: var Compiler, start: int) =
proc emitLoop(self: ref Compiler, start: int) =
self.emitByte(OP_LOOP)
var offset = self.function.chunk.code.len - start + 2
if offset > (int uint16.high):
@ -524,7 +523,7 @@ proc emitLoop(self: var Compiler, start: int) =
self.emitByte(uint8 offset and 0xff)
proc endLooping(self: var Compiler) =
proc endLooping(self: ref Compiler) =
if self.loop.loopEnd != -1:
self.patchJump(self.loop.loopEnd)
self.emitByte(OP_POP)
@ -539,7 +538,7 @@ proc endLooping(self: var Compiler) =
self.loop = self.loop.outer
proc whileStatement(self: var Compiler) =
proc whileStatement(self: ref Compiler) =
var loop = Loop(depth: self.scopeDepth, outer: self.loop, start: self.function.chunk.code.len, alive: true, loopEnd: -1)
self.loop = loop
self.parser.consume(LP, "The loop condition must be parenthesized")
@ -562,7 +561,7 @@ proc whileStatement(self: var Compiler) =
self.endLooping()
proc forStatement(self: var Compiler) =
proc forStatement(self: ref Compiler) =
self.beginScope()
self.parser.consume(LP, "The loop condition must be parenthesized")
if self.parser.peek.kind != EOF:
@ -609,7 +608,7 @@ proc forStatement(self: var Compiler) =
self.endScope()
proc parseBreak(self: var Compiler) =
proc parseBreak(self: ref Compiler) =
if not self.loop.alive:
self.parser.parseError(self.parser.previous, "'break' outside loop")
else:
@ -620,14 +619,14 @@ proc parseBreak(self: var Compiler) =
i -= 1
discard self.emitJump(OP_BREAK)
proc parseAnd(self: var Compiler, canAssign: bool) =
proc parseAnd(self: ref Compiler, canAssign: bool) =
var jump = self.emitJump(OP_JUMP_IF_FALSE)
self.emitByte(OP_POP)
self.parsePrecedence(PREC_AND)
self.patchJump(jump)
proc parseOr(self: var Compiler, canAssign: bool) =
proc parseOr(self: ref Compiler, canAssign: bool) =
var elseJump = self.emitJump(OP_JUMP_IF_FALSE)
var endJump = self.emitJump(OP_JUMP)
self.patchJump(elseJump)
@ -636,7 +635,7 @@ proc parseOr(self: var Compiler, canAssign: bool) =
self.patchJump(endJump)
proc continueStatement(self: var Compiler) =
proc continueStatement(self: ref Compiler) =
if not self.loop.alive:
self.parser.parseError(self.parser.previous, "'continue' outside loop")
else:
@ -648,8 +647,8 @@ proc continueStatement(self: var Compiler) =
self.emitLoop(self.loop.start)
proc parseFunction(self: var Compiler, funType: FunctionType) =
var self = initCompiler(self.vm, funType, addr self, self.parser, self.file)
proc parseFunction(self: ref Compiler, funType: FunctionType) =
var self = initCompiler(self.vm, funType, self, self.parser, self.file)
self.beginScope()
self.parser.consume(LP, "Expecting '(' after function name")
if self.parser.hadError:
@ -688,22 +687,22 @@ proc parseFunction(self: var Compiler, funType: FunctionType) =
self.parser.consume(LB, "Expecting '{' before function body")
self.parseBlock()
var fun = self.endCompiler()
self = self.enclosing[]
if self.function[].chunk.consts.values.len < 255:
self = self.enclosing
if self.function.chunk.consts.values.len < 255:
self.emitBytes(OP_CONSTANT, self.makeConstant(Value(kind: OBJECT, obj: fun)))
else:
self.emitByte(OP_CONSTANT_LONG)
self.emitBytes(self.makeLongConstant(Value(kind: OBJECT, obj: fun)))
proc funDeclaration(self: var Compiler) =
proc funDeclaration(self: ref Compiler) =
var funName = self.parseVariable("expecting function name")
self.markInitialized()
self.parseFunction(FunctionType.FUNC)
self.defineVariable(funName)
proc argumentList(self: var Compiler): uint8 =
proc argumentList(self: ref Compiler): uint8 =
result = 0
if not self.parser.check(RP):
while true:
@ -717,12 +716,12 @@ proc argumentList(self: var Compiler): uint8 =
self.parser.consume(RP, "Expecting ')' after arguments")
proc call(self: var Compiler, canAssign: bool) =
proc call(self: ref Compiler, canAssign: bool) =
var argCount = self.argumentList()
self.emitBytes(OP_CALL, argCount)
proc statement(self: var Compiler) =
proc statement(self: ref Compiler) =
if self.parser.match(TokenType.FOR):
self.forStatement()
elif self.parser.match(IF):
@ -741,7 +740,7 @@ proc statement(self: var Compiler) =
self.expressionStatement()
proc declaration(self: var Compiler) =
proc declaration(self: ref Compiler) =
if self.parser.match(FUN):
self.funDeclaration()
elif self.parser.match(VAR):
@ -811,7 +810,7 @@ proc getRule(kind: TokenType): ParseRule =
result = rules[kind]
proc compile*(self: var Compiler, source: string): ptr Function =
proc compile*(self: ref Compiler, source: string): ptr Function =
var scanner = initLexer(source, self.file)
var tokens = scanner.lex()
if len(tokens) > 1 and not scanner.errored:
@ -827,11 +826,22 @@ proc compile*(self: var Compiler, source: string): ptr Function =
return nil
proc initCompiler*(vm: var VM, context: FunctionType, enclosing: ptr Compiler = nil, parser: Parser = initParser(@[], ""), file: string): Compiler =
result = Compiler(parser: parser, function: nil, locals: @[], scopeDepth: 0, localCount: 0, loop: Loop(alive: false, loopEnd: -1), vm: vm, context: context, enclosing: enclosing, file: file)
proc initCompiler*(vm: ptr VM, context: FunctionType, enclosing: ref Compiler = nil, parser: Parser = initParser(@[], ""), file: string): ref Compiler =
result = new(Compiler)
result.parser = parser
result.function = nil
result.locals = @[]
result.scopeDepth = 0
result.localCount = 0
result.loop = Loop(alive: false, loopEnd: -1)
result.vm = vm
result.context = context
result.enclosing = enclosing
result.file = file
result.parser.file = file
result.locals.add(Local(depth: 0, name: Token(kind: EOF, lexeme: "")))
inc(result.localCount)
result.function = result.markObject(newFunction())
if context != SCRIPT:
result.function.name = newString(enclosing[].parser.previous().lexeme)
result.function.name = newString(enclosing.parser.previous().lexeme)

View File

@ -29,10 +29,17 @@ proc repl(debug: bool = false) =
break
if source == "":
continue
if source == "//clear" or source == "// clear":
echo "\x1Bc"
echo &"JAPL {JAPL_VERSION} ({JAPL_RELEASE}, {CompileDate} {CompileTime})"
echo &"[Nim {NimVersion} on {hostOs} ({hostCPU})]"
continue
else:
var result = bytecodeVM.interpret(source, debug, true, "stdin")
if debug:
echo &"Result: {result}"
if debug:
echo "==== Code ends ===="
proc main(file: string = "", debug: bool = false) =
@ -60,6 +67,7 @@ proc main(file: string = "", debug: bool = false) =
if debug:
echo &"Result: {result}"
bytecodeVM.freeVM(debug)
echo "==== Code ends ===="
when isMainModule:

View File

@ -8,7 +8,6 @@
import ../types/objecttype
import ../types/stringtype
import strformat
import strutils
type
@ -87,25 +86,11 @@ func toFloat*(value: Value): float =
result = value.floatValue
func typeName*(value: Value): string =
case value.kind:
of BOOL, NIL, DOUBLE, INTEGER, NAN, INF:
result = ($value.kind).toLowerAscii()
of MINF:
result = "inf"
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = cast[ptr String](value.obj)[].typeName()
else:
result = value.obj[].typeName()
func toStr*(value: Value): string =
var strObj = cast[ptr String](value.obj)
var c = ""
for i in 0..strObj[].str.len - 1:
c = &"{strObj[].str[i]}"
for i in 0..strObj.str.len - 1:
c = &"{strObj.str[i]}"
result = result & c
@ -124,78 +109,3 @@ func asBool*(b: bool): Value =
proc asStr*(s: string): Value =
result = Value(kind: OBJECT, obj: newString(s))
func isFalsey*(value: Value): bool =
case value.kind:
of BOOL:
result = not value.toBool()
of OBJECT:
result = isFalsey(value.obj[])
of INTEGER:
result = value.toInt() == 0
of DOUBLE:
result = value.toFloat() > 0.0
of NIL:
result = true
of INF, MINF:
result = false
of NAN:
result = true
proc valuesEqual*(a: Value, b: Value): bool =
if a.kind != b.kind:
result = false
else:
case a.kind:
of BOOL:
result = a.toBool() == b.toBool()
of NIL:
result = true
of INTEGER:
result = a.toInt() == b.toInt()
of DOUBLE:
result = a.toFloat() == b.toFloat()
of OBJECT:
case a.obj.kind:
of ObjectTypes.STRING:
var a = cast[ptr String](a.obj)
var b = cast[ptr String](b.obj)
result = valuesEqual(a[], b[])
else:
result = valuesEqual(a.obj[], b.obj[])
of INF:
result = b.kind == INF
of MINF:
result = b.kind == MINF
of NAN:
result = false
proc hashFloat(f: float): uint32 =
result = 2166136261u32
result = result xor uint32 f
result *= 16777619
return result
proc hash*(value: Value): uint32 =
case value.kind:
of INTEGER:
result = uint32 value.toInt()
of BOOL:
if value.boolValue:
result = uint32 1
else:
result = uint32 0
of DOUBLE:
result = hashFloat(value.toFloat())
of OBJECT:
case value.obj.kind:
of ObjectTypes.STRING:
result = hash(cast[ptr String](value.obj)[])
else:
result = hash(value.obj[])
else: # More coming soon
result = uint32 0

View File

@ -10,7 +10,7 @@ type JAPLException* = object of Obj
proc stringify*(self: ptr JAPLException): string =
return &"{self[].errName[].stringify}: {self[].message[].stringify}"
return &"{self.errName.stringify}: {self.message.stringify}"
proc newTypeError*(message: string): ptr JAPLException =

View File

@ -39,12 +39,16 @@ proc isFalsey*(fn: Function): bool =
return false
proc stringify*(fn: Function): string =
proc stringify*(fn: ptr Function): string =
if fn.name != nil:
result = &"<function object '{stringify(fn.name[])}' (built-in type)>"
result = &"<function '{stringify(fn.name)}'>"
else:
result = &"<top-level code object (internal type)>"
result = &"<code object>"
proc valuesEqual*(a, b: Function): bool =
result = a.name[].stringify == b.name[].stringify
proc valuesEqual*(a, b: ptr Function): bool =
result = a.name.stringify == b.name.stringify
proc typeName*(self: ptr Function): string =
result = "function"

View File

@ -12,29 +12,28 @@ type
Obj* = object of RootObj
kind*: ObjectTypes
hashValue*: uint32
next*: ptr Obj
func objType*(obj: Obj): ObjectTypes =
func objType*(obj: ptr Obj): ObjectTypes =
return obj.kind
proc stringify*(obj: Obj): string =
proc stringify*(obj: ptr Obj): string =
result = "<object (built-in type)>"
proc typeName*(obj: Obj): string =
proc typeName*(obj: ptr Obj): string =
result = "object"
proc isFalsey*(obj: Obj): bool =
proc isFalsey*(obj: ptr Obj): bool =
result = false
proc valuesEqual*(a: Obj, b: Obj): bool =
proc valuesEqual*(a: ptr Obj, b: ptr Obj): bool =
result = a.kind == b.kind
proc hash*(self: Obj): uint32 =
proc hash*(self: ptr Obj): uint32 =
result = 2166136261u32
# Add more

View File

@ -4,23 +4,26 @@
# natively ASCII encoded, but soon they will support for unicode.
import objecttype
import strformat
import ../memory
type String* = object of Obj
str*: ptr UncheckedArray[char] #TODO -> Maybe ptr UncheckedArray[array[4, char]]?
str*: ptr UncheckedArray[char] # TODO -> Maybe ptr UncheckedArray[array[4, char]]?
len*: int
proc stringify*(s: String): string =
$s.str
proc stringify*(s: ptr String): string =
result = ""
for i in 0..<s.len:
result = result & (&"{s.str[i]}")
proc isFalsey*(s: String): bool =
proc isFalsey*(s: ptr String): bool =
result = s.len == 0
proc hash*(self: String): uint32 =
proc hash*(self: ptr String): uint32 =
result = 2166136261u32
var i = 0
while i < self.len:
@ -30,7 +33,7 @@ proc hash*(self: String): uint32 =
return result
proc valuesEqual*(a: String, b: String): bool =
proc valuesEqual*(a: ptr String, b: ptr String): bool =
if a.len != b.len:
return false
elif a.hash != b.hash:
@ -48,8 +51,8 @@ proc newString*(str: string): ptr String =
for i in 0..len(str) - 1:
result.str[i] = str[i]
result.len = len(str)
result.hashValue = result[].hash()
result.hashValue = result.hash()
proc typeName*(s: String): string =
proc typeName*(s: ptr String): string =
return "string"

View File

@ -35,13 +35,21 @@ func handleInterrupt() {.noconv.} =
raise newException(KeyboardInterrupt, "Ctrl+C")
proc resetStack*(self: var VM) =
self.stack = new(seq[Value])
self.frames = new(seq[CallFrame])
self.frameCount = 0
self.stackTop = 0
proc error*(self: var VM, error: ptr JAPLException) =
var previous = "" # All this stuff seems overkill, but it makes the traceback look nicer
var repCount = 0 # and if we are here we are far beyond a point where performance matters
var mainReached = false
var output = ""
echo "Traceback (most recent call last):"
for frame in reversed(self.frames):
for frame in reversed(self.frames[]):
if mainReached:
break
var function = frame.function
@ -50,7 +58,7 @@ proc error*(self: var VM, error: ptr JAPLException) =
output = &" File '{self.file}', line {line}, in '<module>':"
mainReached = true
else:
output = &" File '{self.file}', line {line}, in {stringify(function.name[])}():"
output = &" File '{self.file}', line {line}, in {stringify(function.name)}():"
if output != previous:
if repCount > 0:
echo &" ...repeated {repCount} more times..."
@ -60,15 +68,16 @@ proc error*(self: var VM, error: ptr JAPLException) =
else:
repCount += 1
echo error.stringify()
self.resetStack()
proc pop*(self: var VM): Value =
result = self.stack.pop()
result = self.stack[].pop()
self.stackTop -= 1
proc push*(self: var VM, value: Value) =
self.stack.add(value)
self.stack[].add(value)
self.stackTop += 1
@ -76,9 +85,8 @@ proc peek*(self: var VM, distance: int): Value =
return self.stack[self.stackTop - distance - 1]
template markObject*(self, obj: untyped): untyped =
obj.next = self.objects
self.objects = obj
template markObject*(self: ptr VM, obj: untyped): untyped =
self.objects.add(obj)
obj
@ -101,7 +109,7 @@ proc slice(self: var VM): bool =
if idx.toInt() - 1 > len(str) - 1:
self.error(newIndexError("string index out of bounds"))
return false
self.push(Value(kind: OBJECT, obj: self.markObject(newString(&"{str[idx.toInt()]}"))))
self.push(Value(kind: OBJECT, obj: markObject(addr self, newString(&"{str[idx.toInt()]}"))))
return true
else:
@ -144,7 +152,7 @@ proc sliceRange(self: var VM): bool =
elif sliceStart.toInt() > sliceEnd.toInt():
self.error(newIndexError("the start index can't be bigger than the end index"))
return false
self.push(Value(kind: OBJECT, obj: self.markObject(newString(str[sliceStart.toInt()..<sliceEnd.toInt()]))))
self.push(Value(kind: OBJECT, obj: markObject(addr self, newString(str[sliceStart.toInt()..<sliceEnd.toInt()]))))
return true
else:
@ -157,13 +165,13 @@ proc sliceRange(self: var VM): bool =
proc call(self: var VM, function: ptr Function, argCount: uint8): bool =
if argCount != uint8 function.arity:
self.error(newTypeError(&"Function '{stringify(function.name[])}' takes {function.arity} argument(s), got {argCount}"))
self.error(newTypeError(&"Function '{stringify(function.name)}' takes {function.arity} argument(s), got {argCount}"))
return false
if self.frameCount == FRAMES_MAX:
self.error(newRecursionError("Max recursion depth exceeded"))
return false
var frame = CallFrame(function: function, ip: 0, slots: self.stack[argCount..self.stackTop - 1])
self.frames.add(frame)
self.frames[].add(frame)
self.frameCount += 1
return true
@ -275,7 +283,7 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
opcode = OpCode(instruction)
if debug: # Consider moving this elsewhere
stdout.write("Current VM stack status: [")
for v in self.stack:
for v in self.stack[]:
stdout.write(stringify(v))
stdout.write(", ")
stdout.write("]\n")
@ -290,7 +298,7 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
if frame.function.name == nil:
stdout.write(" main\n")
else:
stdout.write(&" function, '{frame.function.name[].stringify()}'\n")
stdout.write(&" function, '{frame.function.name.stringify()}'\n")
stdout.write(&"Current frame count: {self.frameCount}\n")
stdout.write("Current frame stack status: ")
if frame.function.name == nil:
@ -330,7 +338,7 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
if self.peek(0).isStr() and self.peek(1).isStr():
var r = self.pop().toStr()
var l = self.pop().toStr()
self.push(Value(kind: OBJECT, obj: self.markObject(newString(l & r))))
self.push(Value(kind: OBJECT, obj: markObject(addr self, newString(l & r))))
else:
self.error(newTypeError(&"Unsupported binary operator for objects of type '{self.peek(0).typeName()}' and '{self.peek(1).typeName()}'"))
return RUNTIME_ERROR
@ -351,7 +359,7 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
if self.peek(1).isStr():
var r = self.pop().toInt()
var l = self.pop().toStr()
self.push(Value(kind: OBJECT, obj: self.markObject(newString(l.repeat(r)))))
self.push(Value(kind: OBJECT, obj: markObject(addr self, newString(l.repeat(r)))))
else:
self.error(newTypeError(&"Unsupported binary operator for objects of type '{self.peek(0).typeName()}' and '{self.peek(1).typeName()}'"))
return RUNTIME_ERROR
@ -359,7 +367,7 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
if self.peek(0).isStr():
var r = self.pop().toStr()
var l = self.pop().toInt()
self.push(Value(kind: OBJECT, obj: self.markObject(newString(r.repeat(l)))))
self.push(Value(kind: OBJECT, obj: markObject(addr self, newString(r.repeat(l)))))
else:
self.error(newTypeError(&"Unsupported binary operator for objects of type '{self.peek(0).typeName()}' and '{self.peek(1).typeName()}"))
return RUNTIME_ERROR
@ -494,12 +502,12 @@ proc run(self: var VM, debug, repl: bool): InterpretResult =
discard
of OP_RETURN:
var retResult = self.pop()
self.frameCount -= 1
discard self.frames.pop()
if repl:
if not self.lastPop.isNil():
echo stringify(self.lastPop)
self.lastPop = Value(kind: NIL)
self.frameCount -= 1
discard self.frames[].pop()
if self.frameCount == 0:
discard self.pop()
return OK
@ -513,12 +521,13 @@ proc freeObject(obj: ptr Obj, debug: bool) =
of ObjectTypes.STRING:
var str = cast[ptr String](obj)
if debug:
echo &"Freeing string object with value '{stringify(str[])}' of length {str.len}"
echo &"Freeing string object with value '{stringify(str)}' of length {str.len}"
discard freeArray(char, str.str, str.len)
discard free(ObjectTypes.STRING, obj)
of ObjectTypes.FUNCTION:
var fun = cast[ptr Function](obj)
echo "Freeing function object with value '{stringify(fun[])}'"
if debug:
echo &"Freeing function object with value '{stringify(fun)}'"
fun.chunk.freeChunk()
discard free(ObjectTypes.FUNCTION, fun)
else:
@ -526,16 +535,12 @@ proc freeObject(obj: ptr Obj, debug: bool) =
proc freeObjects(self: var VM, debug: bool) =
var obj = self.objects
var next: ptr Obj
var i = 0
while obj != nil:
next = obj[].next
var objCount = len(self.objects)
for obj in reversed(self.objects):
freeObject(obj, debug)
i += 1
obj = next
discard self.objects.pop()
if debug:
echo &"Freed {i} objects"
echo &"Freed {objCount} objects"
proc freeVM*(self: var VM, debug: bool) =
@ -549,25 +554,21 @@ proc freeVM*(self: var VM, debug: bool) =
quit(71)
proc resetStack*(self: var VM) =
self.stack = @[]
self.frames = @[]
self.frameCount = 0
self.stackTop = 0
proc initVM*(): VM =
setControlCHook(handleInterrupt)
result = VM(lastPop: Value(kind: NIL), frameCount: 0, frames: @[], stack: @[], stackTop: 0, objects: nil, globals: initTable[string, Value](), source: "", file: "")
result = VM(lastPop: Value(kind: NIL), frameCount: 0, frames: new(seq[CallFrame]), stack: new(seq[Value]), stackTop: 0, objects: @[], globals: initTable[string, Value](), source: "", file: "")
proc interpret*(self: var VM, source: string, debug: bool = false, repl: bool = false, file: string): InterpretResult =
var compiler = initCompiler(self, SCRIPT, file=file)
self.resetStack()
var compiler = initCompiler(addr self, SCRIPT, file=file)
var compiled = compiler.compile(source)
self.source = source
self.file = file
if compiled == nil:
return COMPILE_ERROR
if compiler.parser.hadError:
return COMPILE_ERROR
return OK
self.push(Value(kind: OBJECT, obj: compiled))
discard self.callValue(Value(kind: OBJECT, obj: compiled), 0)
try:
@ -575,4 +576,3 @@ proc interpret*(self: var VM, source: string, debug: bool = false, repl: bool =
except KeyboardInterrupt:
self.error(newInterruptedError(""))
return RUNTIME_ERROR
self.resetStack()