also derete this #3
|
@ -23,6 +23,7 @@ import strformat
|
|||
import algorithm
|
||||
import parseutils
|
||||
import sequtils
|
||||
import strutils
|
||||
|
||||
|
||||
export ast
|
||||
|
@ -119,6 +120,8 @@ proc expression(self: Compiler, node: ASTNode)
|
|||
proc statement(self: Compiler, node: ASTNode)
|
||||
proc declaration(self: Compiler, node: ASTNode)
|
||||
proc peek(self: Compiler, distance: int = 0): ASTNode
|
||||
proc identifier(self: Compiler, node: IdentExpr)
|
||||
proc varDecl(self: Compiler, node: VarDecl)
|
||||
## End of forward declarations
|
||||
|
||||
## Public getters for nicer error formatting
|
||||
|
@ -475,32 +478,24 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug)")
|
||||
|
||||
|
||||
proc identifier(self: Compiler, node: IdentExpr)
|
||||
|
||||
|
||||
proc declareName(self: Compiler, node: ASTNode) =
|
||||
## Compiles all name declarations (constants, static,
|
||||
## and dynamic)
|
||||
## Compiles all name declarations
|
||||
case node.kind:
|
||||
of varDecl:
|
||||
of NodeKind.varDecl:
|
||||
var node = VarDecl(node)
|
||||
if not node.isStatic:
|
||||
# This emits code for dynamically-resolved variables (i.e. globals declared as dynamic and unresolvable names)
|
||||
self.emitByte(DeclareName)
|
||||
self.emitBytes(self.identifierConstant(IdentExpr(node.name)))
|
||||
else:
|
||||
# Statically resolved variable here. Creates a new Name entry
|
||||
# so that self.identifier emits the proper stack offset
|
||||
if self.names.high() > 16777215:
|
||||
# If someone ever hits this limit in real-world scenarios, I swear I'll
|
||||
# slap myself 100 times with a sign saying "I'm dumb". Mark my words
|
||||
self.error("cannot declare more than 16777215 static variables at a time")
|
||||
self.names.add(Name(depth: self.scopeDepth, name: IdentExpr(node.name),
|
||||
isPrivate: node.isPrivate,
|
||||
owner: "",
|
||||
isConst: node.isConst))
|
||||
self.emitByte(StoreFast)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
# Statically resolved variable here. Creates a new Name entry
|
||||
# so that self.identifier emits the proper stack offset
|
||||
if self.names.high() > 16777215:
|
||||
# If someone ever hits this limit in real-world scenarios, I swear I'll
|
||||
# slap myself 100 times with a sign saying "I'm dumb". Mark my words
|
||||
self.error("cannot declare more than 16777215 static variables at a time")
|
||||
self.names.add(Name(depth: self.scopeDepth, name: IdentExpr(node.name),
|
||||
isPrivate: node.isPrivate,
|
||||
owner: "",
|
||||
isConst: node.isConst))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
of funDecl:
|
||||
var node = FunDecl(node)
|
||||
# Declares the function's name in the
|
||||
|
@ -515,15 +510,13 @@ proc declareName(self: Compiler, node: ASTNode) =
|
|||
if self.names.high() > 16777215:
|
||||
self.error("cannot declare more than 16777215 static variables at a time")
|
||||
self.names.add(Name(depth: self.scopeDepth + 1, isPrivate: true, owner: self.currentModule, isConst: false, name: IdentExpr(argument.name)))
|
||||
self.emitByte(LoadFast)
|
||||
self.emitByte(LoadVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
self.scopeDepth -= 1
|
||||
# TODO: Default arguments and unpacking
|
||||
else:
|
||||
discard # TODO: Classes
|
||||
|
||||
proc varDecl(self: Compiler, node: VarDecl)
|
||||
|
||||
|
||||
proc resolveStatic(self: Compiler, name: IdentExpr,
|
||||
depth: int = self.scopeDepth): Name =
|
||||
|
@ -571,7 +564,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
|||
let index = self.getStaticIndex(node)
|
||||
if index != -1:
|
||||
if index >= 0:
|
||||
self.emitByte(LoadFast) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
self.emitByte(LoadVar) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
self.emitBytes(index.toTriple())
|
||||
else:
|
||||
if self.closedOver.len() == 0:
|
||||
|
@ -581,8 +574,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
|||
self.emitByte(LoadHeap) # Heap-allocated closure variable. Stored in a separate "closure array" in the VM that does not have stack semantics
|
||||
self.emitBytes(self.closedOver.high().toTriple())
|
||||
else:
|
||||
self.emitByte(LoadName) # Resolves by name, at runtime, in a global hashmap. Slowest method
|
||||
self.emitBytes(self.identifierConstant(node))
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
|
||||
|
||||
proc assignment(self: Compiler, node: ASTNode) =
|
||||
|
@ -631,15 +623,10 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
# but that requires variants for stack,
|
||||
# heap, and closure variables and I cba
|
||||
if index != -1:
|
||||
self.emitByte(StoreFast)
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(index.toTriple())
|
||||
else:
|
||||
# Assignment only encompasses variable assignments,
|
||||
# so we can ensure the name is a constant (i.e. an
|
||||
# IdentExpr) instead of an object (which would be
|
||||
# the case with setItemExpr)
|
||||
self.emitByte(StoreName)
|
||||
self.emitBytes(self.makeConstant(name))
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
of setItemExpr:
|
||||
discard
|
||||
# TODO
|
||||
|
@ -749,8 +736,89 @@ proc whileStmt(self: Compiler, node: WhileStmt) =
|
|||
self.emitLoop(start)
|
||||
|
||||
|
||||
proc inferValueType(self: Compiler, node: ASTNode): ASTNode =
|
||||
## Infers the type of a given literal expression
|
||||
case node.kind:
|
||||
of listExpr:
|
||||
return ListExpr(node).valueType
|
||||
of dictExpr:
|
||||
# It's not important that we don't use
|
||||
# valueType here, we just need to return
|
||||
# a non-nil value so we don't error out
|
||||
return DictExpr(node).keyType
|
||||
of intExpr:
|
||||
var node = IntExpr(node)
|
||||
var size = node.token.lexeme.split("'")
|
||||
if len(size) notin 1..2:
|
||||
self.error("invalid state: inferValueType -> invalid size specifier for int")
|
||||
elif size.len() == 1:
|
||||
return newIdentExpr(Token(lexeme: "int"))
|
||||
elif size[1] in ["u64", "i64", "u32", "i32", "f64", "f32", "i32", "u32", "u8", "i8"]:
|
||||
if size[1].startsWith("u"):
|
||||
size[1] = size[1].strip(true, false, {'u'})
|
||||
size[1] = &"uint{size[1]}"
|
||||
elif size[1].startsWith("i"):
|
||||
size[1] = size[1].strip(true, false, {'i'})
|
||||
size[1] = &"int{size[1]}"
|
||||
elif size[1].startsWith("f"):
|
||||
size[1] = size[1].strip(true, false, {'f'})
|
||||
size[1] = &"float{size[1]}"
|
||||
return newIdentExpr(Token(lexeme: size[1]))
|
||||
else:
|
||||
self.error(&"invalid type specifier '{size[1]}' for '{size[0]}'")
|
||||
return newIdentExpr(Token(lexeme: "int"))
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
|
||||
proc inferExprType(self: Compiler, node: ASTNode): ASTNode =
|
||||
## Infers the type of a given expression and
|
||||
## returns it
|
||||
case node.kind:
|
||||
of unaryExpr:
|
||||
return self.inferValueType(UnaryExpr(node).a)
|
||||
of binaryExpr:
|
||||
var node = BinaryExpr(node)
|
||||
var a = self.inferValueType(node.a)
|
||||
var b = self.inferValueType(node.b)
|
||||
# This is obviously not correct, but
|
||||
# this function is only useful as a
|
||||
# first type checking step anyway
|
||||
if a == nil:
|
||||
return b
|
||||
return a
|
||||
of {intExpr, hexExpr, binExpr, octExpr,
|
||||
strExpr, falseExpr, trueExpr, infExpr,
|
||||
nanExpr, floatExpr, nilExpr, listExpr,
|
||||
dictExpr, setExpr, tupleExpr
|
||||
}:
|
||||
return self.inferValueType(node)
|
||||
else:
|
||||
discard # Unreachable
|
||||
|
||||
|
||||
proc inferDeclType(self: Compiler, node: Declaration): ASTNode =
|
||||
## Infers the type of a given declaration if it's
|
||||
## not already defined and returns it
|
||||
case node.kind:
|
||||
of funDecl:
|
||||
var node = FunDecl(node)
|
||||
if node.returnType != nil:
|
||||
return node.returnType
|
||||
of NodeKind.varDecl:
|
||||
var node = VarDecl(node)
|
||||
if node.valueType != nil:
|
||||
return node.valueType
|
||||
else:
|
||||
return self.inferExprType(node.value)
|
||||
else:
|
||||
return # Unreachable
|
||||
|
||||
|
||||
proc expression(self: Compiler, node: ASTNode) =
|
||||
## Compiles all expressions
|
||||
if self.inferExprType(node) == nil:
|
||||
self.error("expression has no type")
|
||||
case node.kind:
|
||||
of getItemExpr:
|
||||
discard # TODO
|
||||
|
@ -874,7 +942,8 @@ proc statement(self: Compiler, node: ASTNode) =
|
|||
## Compiles all statements
|
||||
case node.kind:
|
||||
of exprStmt:
|
||||
self.expression(ExprStmt(node).expression)
|
||||
var expression = ExprStmt(node).expression
|
||||
self.expression(expression)
|
||||
self.emitByte(Pop) # Expression statements discard their value. Their main use case is side effects in function calls
|
||||
of NodeKind.ifStmt:
|
||||
self.ifStmt(IfStmt(node))
|
||||
|
@ -915,42 +984,6 @@ proc statement(self: Compiler, node: ASTNode) =
|
|||
self.expression(node)
|
||||
|
||||
|
||||
proc inferValueType(self: Compiler, node: ASTNode): ASTNode =
|
||||
## Infers the type of a given literal expression
|
||||
case node.kind:
|
||||
of listExpr:
|
||||
return ListExpr(node).valueType
|
||||
of dictExpr:
|
||||
# It's not important that we don't use
|
||||
# valueType here, we just need to return
|
||||
# a non-nil value so we don't error out
|
||||
return DictExpr(node).keyType
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
proc inferExprType(self: Compiler, node: Expression): ASTNode =
|
||||
## Infers the type of a given expression and
|
||||
## returns it
|
||||
# TODO
|
||||
|
||||
|
||||
|
||||
proc inferDeclType(self: Compiler, node: Declaration): ASTNode =
|
||||
## Infers the type of a given declaration if it's
|
||||
## not already defined and returns it
|
||||
case node.kind:
|
||||
of funDecl:
|
||||
var node = FunDecl(node)
|
||||
if node.returnType != nil:
|
||||
return node.returnType
|
||||
of NodeKind.varDecl:
|
||||
var node = VarDecl(node)
|
||||
if node.valueType != nil:
|
||||
return node.valueType
|
||||
else:
|
||||
return # Unreachable
|
||||
|
||||
|
||||
proc varDecl(self: Compiler, node: VarDecl) =
|
||||
## Compiles variable declarations
|
||||
if self.inferDeclType(node) == nil:
|
||||
|
@ -994,7 +1027,6 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
|||
# of boilerplate code to make closures work, but
|
||||
# that's about it
|
||||
|
||||
|
||||
self.emitBytes(OpCode.Nil, OpCode.Return)
|
||||
|
||||
# Currently defer is not functional so we
|
||||
|
|
|
@ -250,7 +250,6 @@ type
|
|||
name*: ASTNode
|
||||
value*: ASTNode
|
||||
isConst*: bool
|
||||
isStatic*: bool
|
||||
isPrivate*: bool
|
||||
isLet*: bool
|
||||
valueType*: ASTNode
|
||||
|
|
|
@ -105,13 +105,8 @@ type
|
|||
PopN, # Pops x elements off the stack (optimization for exiting scopes and returning from functions)
|
||||
## Name resolution/handling
|
||||
LoadAttribute,
|
||||
DeclareName, # Declares a global dynamically bound name in the current scope
|
||||
LoadName, # Loads a dynamically bound variable
|
||||
LoadFast, # Loads a statically bound variable
|
||||
StoreName, # Sets/updates a dynamically bound variable's value
|
||||
StoreFast, # Sets/updates a statically bound variable's value
|
||||
DeleteName, # Unbinds a dynamically bound variable's name from the current scope
|
||||
DeleteFast, # Unbinds a statically bound variable's name from the current scope
|
||||
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
|
||||
## Looping and jumping
|
||||
|
@ -176,11 +171,11 @@ const simpleInstructions* = {OpCode.Return, BinaryAdd, BinaryMultiply,
|
|||
MakeClass, ImplicitReturn}
|
||||
|
||||
# Constant instructions are instructions that operate on the bytecode constant table
|
||||
const constantInstructions* = {LoadConstant, DeclareName, LoadName, StoreName, DeleteName}
|
||||
const constantInstructions* = {LoadConstant, }
|
||||
|
||||
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||
# of 24 bit integers
|
||||
const stackTripleInstructions* = {Call, StoreFast, DeleteFast, LoadFast, LoadHeap, StoreHeap}
|
||||
const stackTripleInstructions* = {Call, StoreVar, LoadVar, LoadHeap, StoreHeap}
|
||||
|
||||
# Stack double instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||
# of 16 bit integers
|
||||
|
|
|
@ -153,9 +153,9 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
|||
a = self.optimizeNode(node.a)
|
||||
b = self.optimizeNode(node.b)
|
||||
if self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and (self.warnings[^1].node == a or self.warnings[^1].node == b):
|
||||
# We can't optimize further, the overflow will be caught in the compiler. We don't return the same node
|
||||
# We can't optimize further: the overflow will be caught in the compiler. We don't return the same node
|
||||
# because optimizeNode might've been able to optimize one of the two operands and we don't know which
|
||||
return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
if node.operator.kind == DoubleEqual:
|
||||
if a.kind in {trueExpr, falseExpr, nilExpr, nanExpr, infExpr}:
|
||||
self.newWarning(equalityWithSingleton, a)
|
||||
|
@ -204,10 +204,10 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
|||
result = newBinaryExpr(a, node.operator, b)
|
||||
except OverflowDefect:
|
||||
self.newWarning(valueOverflow, node)
|
||||
return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
except RangeDefect:
|
||||
# TODO: What warning do we raise here?
|
||||
return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
result = newIntExpr(Token(kind: Integer, lexeme: $z, line: IntExpr(a).literal.line, pos: (start: -1, stop: -1)))
|
||||
elif a.kind == floatExpr or b.kind == floatExpr:
|
||||
var x, y, z: float
|
||||
|
@ -241,7 +241,7 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
|||
of Percentage:
|
||||
z = x mod y
|
||||
else:
|
||||
result = BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)
|
||||
result = newBinaryExpr(a, node.operator, b)
|
||||
except OverflowDefect:
|
||||
self.newWarning(valueOverflow, node)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
|
|
Loading…
Reference in New Issue