Name stropping, added NoOp, minor AST fixes, removed builtin opcodes for most operations
This commit is contained in:
parent
299b359344
commit
776968bffc
|
@ -21,7 +21,7 @@ type
|
|||
objNil, objNan, objInf,
|
||||
objString
|
||||
PeonObject* = ref object of RootObj
|
||||
|
||||
## A generic Peon object
|
||||
kind*: ObjectKind
|
||||
Nil* = ref object of PeonObject
|
||||
Nan* = ref object of PeonObject
|
||||
|
@ -74,3 +74,8 @@ proc newInf*(positive: bool): Inf = Inf(kind: objInf, positive: positive)
|
|||
proc newBool*(value: bool): Bool = Bool(kind: objBool, value: value)
|
||||
proc newInt*(value: int): Int = Int(kind: objInt, value: value)
|
||||
proc newFloat*(value: float): Float = Float(kind: objFloat, value: value)
|
||||
|
||||
|
||||
proc `$`*(self: PeonObject): string =
|
||||
## Stringifies a peon object
|
||||
|
|
@ -89,14 +89,7 @@ proc dispatch*(self: PeonVM) =
|
|||
## Main bytecode dispatch loop
|
||||
var instruction: OpCode
|
||||
while true:
|
||||
stdout.write("[")
|
||||
for i, e in self.stack:
|
||||
stdout.write($(e[]))
|
||||
if i < self.stack.high():
|
||||
stdout.write(", ")
|
||||
echo "]"
|
||||
instruction = OpCode(self.readByte(self.chunk))
|
||||
echo instruction
|
||||
case instruction:
|
||||
of OpCode.True:
|
||||
self.push(self.getBool(true))
|
||||
|
@ -108,10 +101,11 @@ proc dispatch*(self: PeonVM) =
|
|||
self.push(self.getNil())
|
||||
of OpCode.Inf:
|
||||
self.push(self.getInf(true))
|
||||
of UnaryPlus:
|
||||
self.push(self.pop())
|
||||
of OpCode.Return:
|
||||
# TODO
|
||||
return
|
||||
of OpCode.NoOp:
|
||||
continue
|
||||
else:
|
||||
discard
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ type
|
|||
depth: int
|
||||
isPrivate: bool
|
||||
isConst: bool
|
||||
isLet: bool
|
||||
valueType: IdentExpr
|
||||
codePos: int
|
||||
|
||||
|
@ -387,16 +388,16 @@ proc literal(self: Compiler, node: ASTNode) =
|
|||
proc unary(self: Compiler, node: UnaryExpr) =
|
||||
## Compiles unary expressions such as decimal or
|
||||
## bitwise negation
|
||||
self.expression(node.a) # Pushes the operand onto the stack
|
||||
self.expression(node.a) # Pushes the operand onto the stack
|
||||
case node.operator.kind:
|
||||
of Minus:
|
||||
self.emitByte(UnaryNegate)
|
||||
self.emitByte(NoOp)
|
||||
of Plus:
|
||||
self.emitByte(UnaryPlus)
|
||||
self.emitByte(NoOp)
|
||||
of TokenType.LogicalNot:
|
||||
self.emitByte(OpCode.LogicalNot)
|
||||
self.emitByte(NoOp)
|
||||
of Tilde:
|
||||
self.emitByte(UnaryNot)
|
||||
self.emitByte(NoOp)
|
||||
else:
|
||||
self.error(&"invalid AST node of kind {node.kind} at unary(): {node} (This is an internal error and most likely a bug)")
|
||||
|
||||
|
@ -409,48 +410,48 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
self.expression(node.b)
|
||||
case node.operator.kind:
|
||||
of Plus:
|
||||
self.emitByte(BinaryAdd)
|
||||
self.emitByte(NoOp)
|
||||
of Minus:
|
||||
self.emitByte(BinarySubtract)
|
||||
self.emitByte(NoOp)
|
||||
of Star:
|
||||
self.emitByte(BinaryMultiply)
|
||||
self.emitByte(NoOp)
|
||||
of DoubleStar:
|
||||
self.emitByte(BinaryPow)
|
||||
self.emitByte(NoOp)
|
||||
of Percentage:
|
||||
self.emitByte(BinaryMod)
|
||||
self.emitByte(NoOp)
|
||||
of FloorDiv:
|
||||
self.emitByte(BinaryFloorDiv)
|
||||
self.emitByte(NoOp)
|
||||
of Slash:
|
||||
self.emitByte(BinaryDivide)
|
||||
self.emitByte(NoOp)
|
||||
of Ampersand:
|
||||
self.emitByte(BinaryAnd)
|
||||
self.emitByte(NoOp)
|
||||
of Caret:
|
||||
self.emitByte(BinaryXor)
|
||||
self.emitByte(NoOp)
|
||||
of Pipe:
|
||||
self.emitByte(BinaryOr)
|
||||
self.emitByte(NoOp)
|
||||
of As:
|
||||
self.emitByte(BinaryAs)
|
||||
self.emitByte(NoOp)
|
||||
of Is:
|
||||
self.emitByte(BinaryIs)
|
||||
self.emitByte(NoOp)
|
||||
of IsNot:
|
||||
self.emitByte(BinaryIsNot)
|
||||
self.emitByte(NoOp)
|
||||
of Of:
|
||||
self.emitByte(BinaryOf)
|
||||
self.emitByte(NoOp)
|
||||
of RightShift:
|
||||
self.emitByte(BinaryShiftRight)
|
||||
self.emitByte(NoOp)
|
||||
of LeftShift:
|
||||
self.emitByte(BinaryShiftLeft)
|
||||
of TokenType.LessThan:
|
||||
self.emitByte(OpCode.LessThan)
|
||||
of TokenType.GreaterThan:
|
||||
self.emitByte(OpCode.GreaterThan)
|
||||
of TokenType.DoubleEqual:
|
||||
self.emitByte(EqualTo)
|
||||
of TokenType.LessOrEqual:
|
||||
self.emitByte(OpCode.LessOrEqual)
|
||||
of TokenType.GreaterOrEqual:
|
||||
self.emitByte(OpCode.GreaterOrEqual)
|
||||
of TokenType.LogicalAnd:
|
||||
self.emitByte(NoOp)
|
||||
of LessThan:
|
||||
self.emitByte(NoOp)
|
||||
of GreaterThan:
|
||||
self.emitByte(NoOp)
|
||||
of DoubleEqual:
|
||||
self.emitByte(NoOp)
|
||||
of LessOrEqual:
|
||||
self.emitByte(NoOp)
|
||||
of GreaterOrEqual:
|
||||
self.emitByte(NoOp)
|
||||
of LogicalAnd:
|
||||
self.expression(node.a)
|
||||
var jump: int
|
||||
if self.enableOptimizations:
|
||||
|
@ -460,7 +461,7 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
self.emitByte(Pop)
|
||||
self.expression(node.b)
|
||||
self.patchJump(jump)
|
||||
of TokenType.LogicalOr:
|
||||
of LogicalOr:
|
||||
self.expression(node.a)
|
||||
let jump = self.emitJump(JumpIfTrue)
|
||||
self.expression(node.b)
|
||||
|
@ -487,7 +488,8 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
|||
owner: self.currentModule,
|
||||
isConst: node.isConst,
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len()))
|
||||
codePos: self.chunk.code.len(),
|
||||
isLet: node.isLet))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
of funDecl:
|
||||
|
@ -499,7 +501,9 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
|||
isConst: false,
|
||||
owner: self.currentModule,
|
||||
valueType: node.returnType,
|
||||
codePos: self.chunk.code.len()))
|
||||
codePos: self.chunk.code.len(),
|
||||
name: node.name,
|
||||
isLet: false))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
# ... but its arguments in an inner one!
|
||||
|
@ -516,7 +520,8 @@ proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
|||
isConst: false,
|
||||
name: argument.name,
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len()))
|
||||
codePos: self.chunk.code.len(),
|
||||
isLet: false))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
self.scopeDepth -= 1
|
||||
|
@ -608,7 +613,8 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
|||
elif s.isConst:
|
||||
# Constants are emitted as, you guessed it, LoadConstant instructions
|
||||
# no matter the scope depth. If optimizations are enabled, the compiler
|
||||
# will reuse the same constant every time it is referenced instead
|
||||
# will reuse the same constant every time it is referenced instead of
|
||||
# allocating a new one each time
|
||||
self.emitConstant(node)
|
||||
else:
|
||||
self.detectClosureVariable(s.name)
|
||||
|
@ -638,34 +644,38 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
# TODO: This will explode with slicing!
|
||||
var name = IdentExpr(node.name)
|
||||
let r = self.resolve(name)
|
||||
if r != nil and r.isConst:
|
||||
self.error("cannot assign to constant")
|
||||
if r == nil:
|
||||
self.error(&"assignment to undeclared name '{node.name}'")
|
||||
elif r.isConst:
|
||||
self.error(&"cannot assign to '{node.name}'")
|
||||
elif r.isLet:
|
||||
self.error(&"cannot reassign '{node.name}'")
|
||||
self.expression(node.value)
|
||||
let t = self.getStackPos(name)
|
||||
let index = t.pos
|
||||
case node.token.kind:
|
||||
of InplaceAdd:
|
||||
self.emitByte(BinaryAdd)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceSub:
|
||||
self.emitByte(BinarySubtract)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceDiv:
|
||||
self.emitByte(BinaryDivide)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceMul:
|
||||
self.emitByte(BinaryMultiply)
|
||||
self.emitByte(NoOp)
|
||||
of InplacePow:
|
||||
self.emitByte(BinaryPow)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceFloorDiv:
|
||||
self.emitByte(BinaryFloorDiv)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceMod:
|
||||
self.emitByte(BinaryMod)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceAnd:
|
||||
self.emitByte(BinaryAnd)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceXor:
|
||||
self.emitByte(BinaryXor)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceRightShift:
|
||||
self.emitByte(BinaryShiftRight)
|
||||
self.emitByte(NoOp)
|
||||
of InplaceLeftShift:
|
||||
self.emitByte(BinaryShiftLeft)
|
||||
self.emitByte(NoOp)
|
||||
else:
|
||||
discard # Unreachable
|
||||
# In-place operators just change
|
||||
|
@ -677,10 +687,6 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
# that would require variants of each
|
||||
# one for regular stack variables as
|
||||
# well as closed-over ones
|
||||
|
||||
# Cannot be nil, we already resolved this!
|
||||
if self.resolve(name).isConst:
|
||||
self.error("cannot mutate constant")
|
||||
if index != -1:
|
||||
if not t.closedOver:
|
||||
self.emitByte(StoreVar)
|
||||
|
@ -997,7 +1003,7 @@ proc breakStmt(self: Compiler, node: BreakStmt) =
|
|||
|
||||
# Emits dummy jump offset, this is
|
||||
# patched later
|
||||
discard self.emitJump(OpCode.Break)
|
||||
discard self.emitJump(OpCode.Jump)
|
||||
self.currentLoop.breakPos.add(self.chunk.code.high() - 4)
|
||||
if self.currentLoop.depth > self.scopeDepth:
|
||||
# Breaking out of a loop closes its scope
|
||||
|
|
|
@ -82,7 +82,6 @@ proc removeKeyword*(self: SymbolTable, lexeme: string) =
|
|||
self.keywords.del(lexeme)
|
||||
|
||||
|
||||
|
||||
proc existsSymbol*(self: SymbolTable, lexeme: string): bool {.inline.} =
|
||||
## Returns true if a given symbol exists
|
||||
## in the symbol table already
|
||||
|
@ -505,10 +504,14 @@ proc parseBackticks(self: Lexer) =
|
|||
## by backticks. This may be used
|
||||
## for name stropping as well as to
|
||||
## reimplement existing operators
|
||||
## (e.g. +, -, etc.)
|
||||
|
||||
## (e.g. +, -, etc.) without the
|
||||
## parser complaining about syntax
|
||||
## errors
|
||||
while not self.match("`") and not self.done():
|
||||
discard self.step()
|
||||
if self.peek().isAlphaNumeric() or self.symbols.existsSymbol(self.peek()):
|
||||
discard self.step()
|
||||
continue
|
||||
self.error(&"unexpected character: '{self.peek()}'")
|
||||
self.createToken(Identifier)
|
||||
# Strips the backticks
|
||||
self.tokens[^1].lexeme = self.tokens[^1].lexeme[1..^2]
|
||||
|
@ -522,7 +525,7 @@ proc parseIdentifier(self: Lexer) =
|
|||
while (self.peek().isAlphaNumeric() or self.check("_")) and not self.done():
|
||||
discard self.step()
|
||||
let name: string = self.source[self.start..<self.current]
|
||||
if name in self.symbols.keywords:
|
||||
if self.symbols.existsKeyword(name):
|
||||
# It's a keyword!
|
||||
self.createToken(self.symbols.keywords[name])
|
||||
else:
|
||||
|
|
|
@ -101,7 +101,6 @@ type
|
|||
# work properly
|
||||
Declaration* = ref object of ASTNode
|
||||
## A declaration
|
||||
closedOver*: bool
|
||||
pragmas*: seq[Token]
|
||||
Statement* = ref object of Declaration
|
||||
## A statement
|
||||
|
@ -164,9 +163,8 @@ type
|
|||
value*: Expression
|
||||
|
||||
CallExpr* = ref object of Expression
|
||||
callee*: ASTNode # The thing being called
|
||||
arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[
|
||||
name: IdentExpr, value: Expression]]]
|
||||
callee*: Expression # The object being called
|
||||
arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[name: IdentExpr, value: Expression]]]
|
||||
|
||||
UnaryExpr* = ref object of Expression
|
||||
operator*: Token
|
||||
|
@ -194,7 +192,7 @@ type
|
|||
|
||||
SliceExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
ends*: seq[ASTNode]
|
||||
ends*: seq[Expression]
|
||||
|
||||
AssignExpr* = ref object of Expression
|
||||
name*: Expression
|
||||
|
@ -219,12 +217,12 @@ type
|
|||
discard # Unused
|
||||
|
||||
ForEachStmt* = ref object of Statement
|
||||
identifier*: ASTNode
|
||||
expression*: ASTNode
|
||||
body*: ASTNode
|
||||
identifier*: IdentExpr
|
||||
expression*: Expression
|
||||
body*: Statement
|
||||
|
||||
DeferStmt* = ref object of Statement
|
||||
expression*: ASTNode
|
||||
expression*: Expression
|
||||
|
||||
TryStmt* = ref object of Statement
|
||||
body*: Statement
|
||||
|
@ -427,7 +425,7 @@ proc newSetItemExpr*(obj: Expression, name: IdentExpr, value: Expression, token:
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newCallExpr*(callee: ASTNode, arguments: tuple[positionals: seq[Expression],
|
||||
proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[Expression],
|
||||
keyword: seq[tuple[name: IdentExpr, value: Expression]]],
|
||||
token: Token): CallExpr =
|
||||
result = CallExpr(kind: callExpr)
|
||||
|
@ -436,7 +434,7 @@ proc newCallExpr*(callee: ASTNode, arguments: tuple[positionals: seq[Expression]
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newSliceExpr*(expression: Expression, ends: seq[ASTNode],
|
||||
proc newSliceExpr*(expression: Expression, ends: seq[Expression],
|
||||
token: Token): SliceExpr =
|
||||
result = SliceExpr(kind: sliceExpr)
|
||||
result.expression = expression
|
||||
|
@ -579,8 +577,8 @@ proc newIfStmt*(condition: Expression, thenBranch, elseBranch: Statement,
|
|||
|
||||
|
||||
proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
||||
isPrivate: bool = true, token: Token, closedOver: bool,
|
||||
isLet: bool = false, valueType: IdentExpr): VarDecl =
|
||||
isPrivate: bool = true, token: Token, isLet: bool = false,
|
||||
valueType: IdentExpr, pragmas: seq[Token]): VarDecl =
|
||||
result = VarDecl(kind: varDecl)
|
||||
result.name = name
|
||||
result.value = value
|
||||
|
@ -589,11 +587,12 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
|||
result.token = token
|
||||
result.isLet = isLet
|
||||
result.valueType = valueType
|
||||
result.pragmas = pragmas
|
||||
|
||||
|
||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression],
|
||||
body: Statement, isAsync, isGenerator: bool,
|
||||
isPrivate: bool, token: Token, closedOver: bool): FunDecl =
|
||||
isPrivate: bool, token: Token, pragmas: seq[Token]): FunDecl =
|
||||
result = FunDecl(kind: funDecl)
|
||||
result.name = name
|
||||
result.arguments = arguments
|
||||
|
@ -603,7 +602,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
|
|||
result.isGenerator = isGenerator
|
||||
result.isPrivate = isPrivate
|
||||
result.token = token
|
||||
result.closedOver = closedOver
|
||||
result.pragmas = pragmas
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -53,46 +53,11 @@ type
|
|||
# Note: x represents the
|
||||
# argument to unary opcodes, while
|
||||
# a and b represent arguments to binary
|
||||
# opcodes. Other variable names may be
|
||||
# used for more complex opcodes. All
|
||||
# arguments to opcodes (if they take
|
||||
# arguments) come from popping off the
|
||||
# stack
|
||||
# opcodes. Other variable names (c, d, etc.)
|
||||
# may be used for more complex opcodes
|
||||
|
||||
LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack
|
||||
## Binary operators
|
||||
UnaryNegate, # Pushes the result of -x onto the stack
|
||||
UnaryPlus, # Pushes the result of +x onto the stack
|
||||
BinaryAdd, # Pushes the result of a + b onto the stack
|
||||
BinarySubtract, # Pushes the result of a - b onto the stack
|
||||
BinaryDivide, # Pushes the result of a / b onto the stack (true division)
|
||||
BinaryFloorDiv, # Pushes the result of a // b onto the stack (integer division)
|
||||
BinaryMultiply, # Pushes the result of a * b onto the stack
|
||||
BinaryPow, # Pushes the result of a ** b (a to the power of b) onto the stack
|
||||
BinaryMod, # Pushes the result of a % b onto the stack (modulo division)
|
||||
BinaryShiftRight, # Pushes the result of a >> b (a with bits shifted b times to the right) onto the stack
|
||||
BinaryShiftLeft, # Pushes the result of a << b (a with bits shifted b times to the left) onto the stack
|
||||
BinaryXor, # Pushes the result of a ^ b (bitwise exclusive or) onto the stack
|
||||
BinaryOr, # Pushes the result of a | b (bitwise or) onto the stack
|
||||
BinaryAnd, # Pushes the result of a & b (bitwise and) onto the stack
|
||||
UnaryNot, # Pushes the result of ~x (bitwise not) onto the stack
|
||||
BinaryAs, # Pushes the result of a as b onto the stack (converts a to the type of b. Explicit support from a is required)
|
||||
BinaryIs, # Pushes the result of a is b onto the stack (true if a and b point to the same object, false otherwise)
|
||||
BinaryIsNot, # Pushes the result of not (a is b). This could be implemented in terms of BinaryIs, but it's more efficient this way
|
||||
BinaryOf, # Pushes the result of a of b onto the stack (true if a is a subclass of b, false otherwise)
|
||||
BinarySlice, # Perform slicing on supported objects (like "hello"[0:2], which yields "he"). The result is pushed onto the stack
|
||||
BinarySubscript, # Subscript operator, like "hello"[0] (which pushes 'h' onto the stack)
|
||||
## Binary comparison operators
|
||||
GreaterThan, # Pushes the result of a > b onto the stack
|
||||
LessThan, # Pushes the result of a < b onto the stack
|
||||
EqualTo, # Pushes the result of a == b onto the stack
|
||||
NotEqualTo, # Pushes the result of a != b onto the stack (optimization for not (a == b))
|
||||
GreaterOrEqual, # Pushes the result of a >= b onto the stack
|
||||
LessOrEqual, # Pushes the result of a <= b onto the stack
|
||||
## Logical operators
|
||||
LogicalNot, # Pushes true onto the stack if x is falsey
|
||||
LogicalAnd, # Pushes true onto the stack if a and b are truthy and false otherwise
|
||||
LogicalOr, # Pushes true onto the stack if either a or b are truthy and false otherwise
|
||||
## Constant opcodes (each of them pushes a singleton on the stack)
|
||||
## Constant opcodes (each of them pushes a constant singleton on the stack)
|
||||
Nil,
|
||||
True,
|
||||
False,
|
||||
|
@ -104,19 +69,18 @@ type
|
|||
PopN, # Pops x elements off the stack (optimization for exiting scopes and returning from functions)
|
||||
## Name resolution/handling
|
||||
LoadAttribute,
|
||||
LoadVar, # Loads a variable from the stack
|
||||
StoreVar, # Sets/updates a statically bound variable's value
|
||||
LoadHeap, # Loads a closed-over variable
|
||||
StoreHeap, # Stores a closed-over variable
|
||||
LoadVar, # Pushes the object at position x in the stack
|
||||
StoreVar, # Stores the value of b at position a in the stack
|
||||
LoadHeap, # Pushes the object position x in the closure array
|
||||
StoreHeap, # Stores the value of b at position a in the closure array
|
||||
## Looping and jumping
|
||||
Jump, # Absolute, unconditional jump into the bytecode
|
||||
JumpIfFalse, # Jumps to an absolute index in the bytecode if the value at the top of the stack is falsey
|
||||
JumpIfTrue, # Jumps to an absolute index in the bytecode if the value at the top of the stack is truthy
|
||||
Jump, # Absolute, unconditional jump into the bytecode at offset x
|
||||
JumpIfFalse, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is falsey
|
||||
JumpIfTrue, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is truthy
|
||||
JumpIfFalsePop, # Like JumpIfFalse, but it also pops off the stack (regardless of truthyness). Optimization for if statements
|
||||
JumpIfFalseOrPop, # Jumps to an absolute index in the bytecode if the value at the top of the stack is falsey and pops it otherwise
|
||||
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||
JumpBackwards, # Relative, unconditional, negative jump into the bytecode
|
||||
Break, # Temporary opcode used to signal exiting out of loops
|
||||
JumpIfFalseOrPop, # Jumps to an absolute index (x) in the bytecode if the value at the top of the stack is falsey and pops it otherwise
|
||||
JumpForwards, # Relative, unconditional, positive jump of size x in the bytecode
|
||||
JumpBackwards, # Relative, unconditional, negative jump of size x in the bytecode
|
||||
## Long variants of jumps (they use a 24-bit operand instead of a 16-bit one)
|
||||
LongJump,
|
||||
LongJumpIfFalse,
|
||||
|
@ -126,11 +90,10 @@ type
|
|||
LongJumpForwards,
|
||||
LongJumpBackwards,
|
||||
## Functions
|
||||
Call, # Calls a callable object
|
||||
Call, # Calls a function
|
||||
Return # Returns from the current function
|
||||
## Exception handling
|
||||
Raise, # Raises exception x
|
||||
ReRaise, # Re-raises active exception
|
||||
Raise, # Raises exception x or re-raises active exception if x is nil
|
||||
BeginTry, # Initiates an exception handling context
|
||||
FinishTry, # Closes the current exception handling context
|
||||
## Generators
|
||||
|
@ -144,30 +107,20 @@ type
|
|||
BuildTuple,
|
||||
## Misc
|
||||
Assert, # Raises an AssertionFailed exception if the value at the top of the stack is falsey
|
||||
MakeClass, # Builds a class instance from the values at the top of the stack (class object, constructor arguments, etc.)
|
||||
Slice, # Slices an object (takes 3 arguments: start, stop, step). Pushes the result of a.subscript(b, c, d) onto the stack
|
||||
GetItem, # Pushes the result of a.getItem(b) onto the stack
|
||||
ImplicitReturn, # Optimization for returning nil from functions (saves us a VM "clock cycle")
|
||||
NoOp, # Just a no-op
|
||||
|
||||
|
||||
# We group instructions by their operation/operand types for easier handling when debugging
|
||||
|
||||
# Simple instructions encompass:
|
||||
# - Instructions that push onto/pop off the stack unconditionally (True, False, Pop, etc.)
|
||||
# - Unary and binary operators
|
||||
const simpleInstructions* = {OpCode.Return, BinaryAdd, BinaryMultiply,
|
||||
BinaryDivide, BinarySubtract,
|
||||
BinaryMod, BinaryPow, OpCode.Nil,
|
||||
OpCode.True, OpCode.False, OpCode.Nan, OpCode.Inf,
|
||||
BinaryShiftLeft, BinaryShiftRight,
|
||||
BinaryXor, OpCode.LogicalNot, EqualTo,
|
||||
OpCode.GreaterThan, OpCode.LessThan, LoadAttribute,
|
||||
BinarySlice, Pop, UnaryNegate,
|
||||
BinaryIs, BinaryAs, OpCode.GreaterOrEqual,
|
||||
OpCode.LessOrEqual, BinaryOr, BinaryAnd,
|
||||
UnaryNot, BinaryFloorDiv, BinaryOf, OpCode.Raise,
|
||||
ReRaise, BeginTry, FinishTry, OpCode.Yield, OpCode.Await,
|
||||
MakeClass, ImplicitReturn, UnaryPlus}
|
||||
# Simple instructions encompass instructions that push onto/pop off the stack unconditionally (True, False, Pop, etc.)
|
||||
const simpleInstructions* = {OpCode.Return, OpCode.Nil,
|
||||
OpCode.True, OpCode.False,
|
||||
OpCode.Nan, OpCode.Inf,
|
||||
Pop, OpCode.Raise,
|
||||
BeginTry, FinishTry,
|
||||
OpCode.Yield, OpCode.Await}
|
||||
|
||||
# Constant instructions are instructions that operate on the bytecode constant table
|
||||
const constantInstructions* = {LoadConstant, }
|
||||
|
@ -184,9 +137,11 @@ const stackDoubleInstructions* = {}
|
|||
const argumentDoubleInstructions* = {PopN, }
|
||||
|
||||
# Jump instructions jump at relative or absolute bytecode offsets
|
||||
const jumpInstructions* = {JumpIfFalse, JumpIfFalsePop, JumpForwards, JumpBackwards,
|
||||
LongJumpIfFalse, LongJumpIfFalsePop, LongJumpForwards,
|
||||
LongJumpBackwards, JumpIfTrue, LongJumpIfTrue}
|
||||
const jumpInstructions* = {JumpIfFalse, JumpIfFalsePop,
|
||||
JumpForwards, JumpBackwards,
|
||||
LongJumpIfFalse, LongJumpIfFalsePop,
|
||||
LongJumpForwards, LongJumpBackwards,
|
||||
JumpIfTrue, LongJumpIfTrue}
|
||||
|
||||
# Collection instructions push a built-in collection type onto the stack
|
||||
const collectionInstructions* = {BuildList, BuildDict, BuildSet, BuildTuple}
|
||||
|
|
|
@ -39,6 +39,7 @@ type
|
|||
Foreach, Yield, Public, As,
|
||||
Of, Defer, Try, Except, Finally,
|
||||
Type, Operator, Case, Enum, From,
|
||||
Emit
|
||||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
|
|
|
@ -375,10 +375,10 @@ proc call(self: Parser): Expression =
|
|||
elif self.match(LeftBracket):
|
||||
# Slicing such as a[1:2]
|
||||
let tok = self.peek(-1)
|
||||
var ends: seq[ASTNode] = @[]
|
||||
var ends: seq[Expression] = @[]
|
||||
while not self.check(RightBracket) and not self.done():
|
||||
if self.check(Colon):
|
||||
ends.add(newNilExpr(Token()))
|
||||
ends.add(newNilExpr(Token(lexeme: "nil")))
|
||||
discard self.step()
|
||||
else:
|
||||
ends.add(self.expression())
|
||||
|
@ -624,7 +624,7 @@ proc yieldStmt(self: Parser): Statement =
|
|||
if not self.check(Semicolon):
|
||||
result = newYieldStmt(self.expression(), tok)
|
||||
else:
|
||||
result = newYieldStmt(newNilExpr(Token()), tok)
|
||||
result = newYieldStmt(newNilExpr(Token(lexeme: "nil")), tok)
|
||||
endOfLine("missing semicolon after yield statement")
|
||||
|
||||
|
||||
|
@ -777,7 +777,7 @@ proc forStmt(self: Parser): Statement =
|
|||
if condition == nil:
|
||||
## An empty condition is functionally
|
||||
## equivalent to "true"
|
||||
condition = newTrueExpr(Token())
|
||||
condition = newTrueExpr(Token(lexeme: "true"))
|
||||
# We can use a while loop, which in this case works just as well
|
||||
body = newWhileStmt(condition, body, tok)
|
||||
if initializer != nil:
|
||||
|
@ -846,15 +846,15 @@ proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declarat
|
|||
else:
|
||||
if tok.kind != Var:
|
||||
self.error(&"{tok.lexeme} declaration requires an initializer")
|
||||
value = newNilExpr(Token())
|
||||
value = newNilExpr(Token(lexeme: "nil"))
|
||||
self.expect(Semicolon, &"expecting semicolon after declaration")
|
||||
case tok.kind:
|
||||
of Var:
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, closedOver=false, valueType=valueType)
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, valueType=valueType, pragmas=(@[]))
|
||||
of Const:
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, isConst=true, closedOver=false, valueType=valueType)
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, isConst=true, valueType=valueType, pragmas=(@[]))
|
||||
of Let:
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, isLet=isLet, closedOver=false, valueType=valueType)
|
||||
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, isLet=isLet, valueType=valueType, pragmas=(@[]))
|
||||
else:
|
||||
discard # Unreachable
|
||||
|
||||
|
@ -868,7 +868,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
var returnType: IdentExpr
|
||||
if not isLambda and self.check(Identifier):
|
||||
# We do this extra check because we might
|
||||
# be called from a contexst where it's
|
||||
# be called from a context where it's
|
||||
# ambiguous whether we're parsing a declaration
|
||||
# or an expression. Fortunately anonymous functions
|
||||
# are nameless, so we can sort the ambiguity by checking
|
||||
|
@ -877,11 +877,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
self.checkDecl(not self.check(Star))
|
||||
self.currentFunction = newFunDecl(nil, arguments, defaults, newBlockStmt(@[], Token()),
|
||||
isAsync=isAsync, isGenerator=isGenerator, isPrivate=true,
|
||||
token=tok, closedOver=false)
|
||||
token=tok, pragmas=(@[]))
|
||||
FunDecl(self.currentFunction).name = newIdentExpr(self.peek(-1))
|
||||
if self.match(Star):
|
||||
FunDecl(self.currentFunction).isPrivate = false
|
||||
elif not isLambda and self.check([LeftBrace, Colon]):
|
||||
elif not isLambda and self.check([LeftBrace, Colon, LeftParen]):
|
||||
# We do a bit of hacking to pretend we never
|
||||
# wanted to parse this as a declaration in
|
||||
# the first place and pass control over to
|
||||
|
@ -904,7 +904,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
self.expect([Identifier, Nil], "expecting function return type after ':'")
|
||||
returnType = newIdentExpr(self.peek(-1))
|
||||
if not self.match(LeftBrace):
|
||||
# Argument-less function
|
||||
var parameter: tuple[name: IdentExpr, valueType: IdentExpr]
|
||||
self.expect(LeftParen)
|
||||
while not self.check(RightParen):
|
||||
|
@ -1048,8 +1047,14 @@ proc declaration(self: Parser): Declaration =
|
|||
of Operator:
|
||||
discard self.step()
|
||||
result = self.funDecl(isOperator=true)
|
||||
of Type, Comment, TokenType.Whitespace, TokenType.Tab:
|
||||
of Type, TokenType.Whitespace, TokenType.Tab:
|
||||
discard self.step() # TODO
|
||||
of Comment:
|
||||
let tok = self.peek()
|
||||
if tok.lexeme.startsWith("#pragma["):
|
||||
discard # TODO: Pragmas
|
||||
elif tok.lexeme.startsWith("##"):
|
||||
discard # TODO: Docstrings
|
||||
else:
|
||||
result = Declaration(self.statement())
|
||||
|
||||
|
|
Loading…
Reference in New Issue