mirror of https://github.com/japl-lang/japl.git
Working on the bytecode VM
This commit is contained in:
parent
76eb517d98
commit
ab6cb16e42
|
@ -93,7 +93,7 @@ proc parseString(self: var Lexer, delimiter: char) =
|
|||
if self.done():
|
||||
raise newException(ParseError, &"Unterminated string literal at {self.line}")
|
||||
discard self.step()
|
||||
let value = StrValue(value: self.source[self.start..<self.current - 1]) # Get the value between quotes
|
||||
let value = Value(kind: ValueTypes.STRING, stringValue: self.source[self.start..<self.current - 1]) # Get the value between quotes
|
||||
let token = self.createToken(STR, value)
|
||||
self.tokens.add(token)
|
||||
|
||||
|
@ -105,11 +105,11 @@ proc parseNumber(self: var Lexer) =
|
|||
discard self.step()
|
||||
while self.peek().isDigit():
|
||||
discard self.step()
|
||||
var value = FloatValue(value: parseFloat(self.source[self.start..<self.current]))
|
||||
self.tokens.add(self.createToken(FLOAT, value))
|
||||
var value = Value(kind: ValueTypes.FLOAT, floatValue: parseFloat(self.source[self.start..<self.current]))
|
||||
self.tokens.add(self.createToken(TokenType.FLOAT, value))
|
||||
else:
|
||||
var value = IntValue(value: parseInt(self.source[self.start..<self.current]))
|
||||
self.tokens.add(self.createToken(INT, value))
|
||||
var value = Value(kind: ValueTypes.INT, intValue: parseInt(self.source[self.start..<self.current]))
|
||||
self.tokens.add(self.createToken(TokenType.INT, value))
|
||||
|
||||
|
||||
proc parseIdentifier(self: var Lexer) =
|
||||
|
@ -118,9 +118,9 @@ proc parseIdentifier(self: var Lexer) =
|
|||
var text: string = self.source[self.start..<self.current]
|
||||
var keyword = text in RESERVED
|
||||
if keyword:
|
||||
self.tokens.add(self.createToken(RESERVED[text], StrValue(value: text)))
|
||||
self.tokens.add(self.createToken(RESERVED[text], Value(kind: ValueTypes.STRING, stringValue: text)))
|
||||
else:
|
||||
self.tokens.add(self.createToken(ID, StrValue(value: text)))
|
||||
self.tokens.add(self.createToken(ID, Value(kind: ValueTypes.STRING, stringValue: text)))
|
||||
|
||||
|
||||
proc parseComment(self: var Lexer) =
|
||||
|
@ -160,17 +160,17 @@ proc scanToken(self: var Lexer) =
|
|||
elif single == '/' and self.match('*'):
|
||||
self.parseComment()
|
||||
elif single == '=' and self.match('='):
|
||||
self.tokens.add(self.createToken(DEQ, StrValue(value: "==")))
|
||||
self.tokens.add(self.createToken(DEQ, Value(kind: ValueTypes.STRING, stringValue: "==")))
|
||||
elif single == '>' and self.match('='):
|
||||
self.tokens.add(self.createToken(GE, StrValue(value: ">=")))
|
||||
self.tokens.add(self.createToken(GE, Value(kind: ValueTypes.STRING, stringValue: ">=")))
|
||||
elif single == '<' and self.match('='):
|
||||
self.tokens.add(self.createToken(LE, StrValue(value: "<=")))
|
||||
self.tokens.add(self.createToken(LE, Value(kind: ValueTypes.STRING, stringValue: "<=")))
|
||||
elif single == '!' and self.match('='):
|
||||
self.tokens.add(self.createToken(NE, StrValue(value: "!=")))
|
||||
self.tokens.add(self.createToken(NE, Value(kind: ValueTypes.STRING, stringValue: "!=")))
|
||||
elif single == '*' and self.match('*'):
|
||||
self.tokens.add(self.createToken(POW, StrValue(value: "**")))
|
||||
self.tokens.add(self.createToken(POW, Value(kind: ValueTypes.STRING, stringValue: "**")))
|
||||
else:
|
||||
self.tokens.add(self.createToken(TOKENS[single], CharValue(value: single)))
|
||||
self.tokens.add(self.createToken(TOKENS[single], Value(kind: ValueTypes.CHAR, charValue: single)))
|
||||
else:
|
||||
raise newException(ParseError, &"Unexpected character '{single}' at {self.line}")
|
||||
|
||||
|
@ -179,10 +179,6 @@ proc lex*(self: var Lexer): seq[Token] =
|
|||
while not self.done():
|
||||
self.start = self.current
|
||||
self.scanToken()
|
||||
self.tokens.add(Token(kind: EOF, lexeme: "EOF", literal: IntValue(value: -1), line: self.line))
|
||||
self.tokens.add(Token(kind: EOF, lexeme: "EOF", literal: Value(kind: CHAR, charValue: '\0'), line: self.line))
|
||||
return self.tokens
|
||||
|
||||
|
||||
var lexer = initLexer("print(1);")
|
||||
var tokens = lexer.lex()
|
||||
assert tokens[0].literal.value == "print"
|
28
nim/main.nim
28
nim/main.nim
|
@ -0,0 +1,28 @@
|
|||
import vm
|
||||
import meta/chunk
|
||||
import util/debug
|
||||
import meta/valueobject
|
||||
import strformat
|
||||
|
||||
|
||||
proc main() =
|
||||
echo "Initializing the JAPL virtual machine"
|
||||
var bytecodeVM = initVM()
|
||||
echo "Creating arbitrary chunk"
|
||||
var chunk: Chunk = initChunk()
|
||||
var index: int = chunk.addConstant(Value(kind: FLOAT, floatValue: 1.2))
|
||||
chunk.writeChunk(uint8 OP_CONSTANT, 1)
|
||||
chunk.writeChunk(uint8 index, 1)
|
||||
chunk.writeChunk(uint8 OP_RETURN, 1)
|
||||
echo "Disassembling chunk"
|
||||
chunk.disassembleChunk("test chunk")
|
||||
echo "Interpreting bytecode instructions"
|
||||
echo fmt"Result: {bytecodeVM.interpret(chunk)}"
|
||||
bytecodeVM.freeVM()
|
||||
chunk.freeChunk()
|
||||
|
||||
|
||||
|
||||
when isMainModule:
|
||||
main()
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import valueobject
|
||||
|
||||
|
||||
type
|
||||
OpCode* = enum
|
||||
OP_CONSTANT = 0u8,
|
||||
OP_CONSTANT_LONG,
|
||||
OP_RETURN,
|
||||
Chunk* = ref object
|
||||
consts*: ValueArray
|
||||
code*: seq[uint8]
|
||||
lines*: seq[int]
|
||||
|
||||
|
||||
proc initChunk*(): Chunk =
|
||||
result = Chunk(consts: initValueArray(), code: @[], lines: @[])
|
||||
|
||||
|
||||
proc writeChunk*(self: Chunk, byte: uint8, line: int) =
|
||||
self.code.add(byte)
|
||||
self.lines.add(line)
|
||||
|
||||
proc freeChunk*(self: var Chunk) =
|
||||
self.consts = ValueArray(values: @[])
|
||||
self.code = @[]
|
||||
self.lines = @[]
|
||||
|
||||
|
||||
proc addConstant*(chunk: var Chunk, constant: Value): int =
|
||||
chunk.consts.values.add(constant)
|
||||
return len(chunk.consts.values) - 1 # The index of the constant
|
|
@ -1,13 +1,38 @@
|
|||
# Value objects
|
||||
import strformat
|
||||
|
||||
type
|
||||
Value*[T] = ref object of RootObj
|
||||
value*: T
|
||||
StrValue* = ref object of Value
|
||||
value*: string
|
||||
CharValue* = ref object of Value
|
||||
value*: char
|
||||
IntValue* = ref object of Value
|
||||
value*: int
|
||||
FloatValue* = ref object of Value
|
||||
value*: float
|
||||
ValueTypes* = enum
|
||||
FLOAT, INT, CHAR, STRING
|
||||
Value* = ref object of RootObj
|
||||
case kind*: ValueTypes
|
||||
of CHAR:
|
||||
charValue*: char
|
||||
of STRING:
|
||||
stringValue*: string
|
||||
of FLOAT:
|
||||
floatValue*: float
|
||||
of INT:
|
||||
intValue*: int
|
||||
ValueArray* = ref object
|
||||
values*: seq[Value]
|
||||
|
||||
|
||||
proc initValueArray*(): ValueArray =
|
||||
result = ValueArray(values: @[])
|
||||
|
||||
|
||||
proc writeValueArray*(arr: var ValueArray, value: Value) =
|
||||
arr.values.add(value)
|
||||
|
||||
proc printValue*(value: Value) =
|
||||
case value.kind:
|
||||
of FLOAT:
|
||||
echo &"\tValue: {value.floatValue}\n\tKind: {value.kind}"
|
||||
of STRING:
|
||||
echo &"\tValue: {value.stringValue}\n\tKind: {value.kind}"
|
||||
of INT:
|
||||
echo &"\tValue: {value.intValue}\n\tKind: {value.kind}"
|
||||
of CHAR:
|
||||
echo &"\tValue: {value.charValue}\n\tKind: {value.kind}"
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import ../meta/chunk
|
||||
import ../meta/valueobject
|
||||
import strformat
|
||||
|
||||
|
||||
proc simpleInstruction(name: string, index: int): int =
|
||||
var index = index
|
||||
echo &"\tOpCode at offset: {name}"
|
||||
echo ""
|
||||
return index + 1
|
||||
|
||||
|
||||
proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
var constant = chunk.code[offset + 1]
|
||||
echo &"\tOpCode at offset: {name}, points to index {constant}"
|
||||
printValue(chunk.consts.values[constant])
|
||||
echo ""
|
||||
return offset + 2
|
||||
|
||||
|
||||
proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
||||
echo &"Current offset: {offset}\nCurrent line: {chunk.lines[offset]}"
|
||||
var opcode = OpCode(chunk.code[offset])
|
||||
if opcode == OP_RETURN:
|
||||
simpleInstruction("OP_RETURN", offset)
|
||||
elif opcode == OP_CONSTANT:
|
||||
constantInstruction("OP_CONSTANT", chunk, offset)
|
||||
else:
|
||||
echo &"Unknown opcode {opcode} at index {offset}"
|
||||
return offset + 1
|
||||
|
||||
|
||||
|
||||
proc disassembleChunk*(chunk: Chunk, name: string) =
|
||||
echo &"==== JAPL VM Debugger - Chunk '{name}' ====\n"
|
||||
var index = 0
|
||||
while index < chunk.code.len:
|
||||
index = disassembleInstruction(chunk, index)
|
||||
echo &"==== Debug session ended - Chunk '{name}' ===="
|
45
nim/vm.nim
45
nim/vm.nim
|
@ -0,0 +1,45 @@
|
|||
import meta/chunk
|
||||
|
||||
|
||||
type InterpretResult = enum
|
||||
OK,
|
||||
COMPILE_ERROR,
|
||||
RUNTIME_ERROR
|
||||
|
||||
type VM = ref object
|
||||
chunk: Chunk
|
||||
ip: int
|
||||
|
||||
|
||||
proc run(self: VM): InterpretResult =
|
||||
template readByte: untyped =
|
||||
inc(self.ip)
|
||||
self.chunk.code[self.ip - 1]
|
||||
|
||||
var instruction: uint8
|
||||
var opcode: OpCode
|
||||
while true:
|
||||
{.computedgoto.}
|
||||
instruction = readByte()
|
||||
opcode = OpCode(instruction)
|
||||
case opcode:
|
||||
of OP_RETURN:
|
||||
return OK
|
||||
of OP_CONSTANT:
|
||||
return OK
|
||||
of OP_CONSTANT_LONG:
|
||||
return OK
|
||||
|
||||
|
||||
proc interpret*(self: var VM, chunk: var Chunk): InterpretResult =
|
||||
self.chunk = chunk
|
||||
self.ip = 0
|
||||
result = self.run()
|
||||
|
||||
|
||||
proc initVM*(): VM =
|
||||
result = VM(chunk: initChunk(), ip: 0)
|
||||
|
||||
|
||||
proc freeVM*(self: VM) =
|
||||
return
|
Loading…
Reference in New Issue