mirror of https://github.com/japl-lang/japl.git
Minor bug fixes to chunks, function calls are still broken
This commit is contained in:
parent
0fd7cea1e1
commit
cb384aaf5f
122
nim/common.nim
122
nim/common.nim
|
@ -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)
|
||||
|
|
148
nim/compiler.nim
148
nim/compiler.nim
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
78
nim/vm.nim
78
nim/vm.nim
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue