Completely rewrote AST with a less flexible, but easier to read and understand design
This commit is contained in:
parent
3b287aad7f
commit
a2caa6214f
|
@ -15,10 +15,11 @@
|
||||||
## An Abstract Syntax Tree (AST) structure for our recursive-descent
|
## An Abstract Syntax Tree (AST) structure for our recursive-descent
|
||||||
## top-down parser. For more info, check out docs/grammar.md
|
## top-down parser. For more info, check out docs/grammar.md
|
||||||
|
|
||||||
import token
|
|
||||||
|
|
||||||
import strformat
|
import strformat
|
||||||
import strutils
|
|
||||||
|
|
||||||
|
import token
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -30,11 +31,9 @@ type
|
||||||
# Declarations
|
# Declarations
|
||||||
classDecl = 0u8,
|
classDecl = 0u8,
|
||||||
funDecl,
|
funDecl,
|
||||||
asyncFunDecl,
|
|
||||||
varDecl,
|
varDecl,
|
||||||
# Statements
|
# Statements
|
||||||
forStmt, # Unused for now (for loops are compiled to while loops)
|
forStmt, # Unused for now (for loops are compiled to while loops)
|
||||||
foreachStmt,
|
|
||||||
ifStmt,
|
ifStmt,
|
||||||
returnStmt,
|
returnStmt,
|
||||||
breakStmt,
|
breakStmt,
|
||||||
|
@ -74,34 +73,110 @@ type
|
||||||
|
|
||||||
ASTNode* = ref object
|
ASTNode* = ref object
|
||||||
## An AST node
|
## An AST node
|
||||||
token*: Token
|
|
||||||
kind*: NodeKind
|
|
||||||
# This makes our life easier
|
|
||||||
# because we don't need object
|
|
||||||
# variants or inheritance to
|
|
||||||
# determine how many (if any)
|
|
||||||
# child nodes we need/have. The
|
|
||||||
# functions processing the AST
|
|
||||||
# node will take care of that
|
|
||||||
children*: seq[ASTNode]
|
|
||||||
pos*: tuple[start, stop: int]
|
pos*: tuple[start, stop: int]
|
||||||
|
case kind*: NodeKind
|
||||||
|
of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr:
|
||||||
|
# This makes it much easier to handle numeric types, as
|
||||||
|
# there is no overflow/underflow or precision to deal with.
|
||||||
|
# Numbers are just serialized as strings and then converted
|
||||||
|
# before being passed to the VM, which also makes it easier
|
||||||
|
# to implement a potential bignum arithmetic that is compatible
|
||||||
|
# with machine types, i.e. if a type fits into 8, 16, 32 or 64 bits
|
||||||
|
# then it is stored in such a type to save space, else it will be
|
||||||
|
# converted to a bigint. Bigfloats with arbitrary-precision arithmetic
|
||||||
|
# would also be nice, although arguably less useful
|
||||||
|
literal*: Token
|
||||||
|
of identExpr:
|
||||||
|
name*: Token
|
||||||
|
of groupingExpr:
|
||||||
|
wrapped*: ASTNode
|
||||||
|
# Sadly nim doesn't allow to re-declare
|
||||||
|
# a field in a case statement yet, even if it
|
||||||
|
# is of the same type.
|
||||||
|
# Check out https://github.com/nim-lang/RFCs/issues/368
|
||||||
|
# for more info, but currently this is the least
|
||||||
|
# ugly workaround
|
||||||
|
of getExpr:
|
||||||
|
getObj*: ASTNode
|
||||||
|
getName*: ASTNode
|
||||||
|
of setExpr:
|
||||||
|
setObj*: ASTNode
|
||||||
|
setName*: ASTNode
|
||||||
|
setValue*: ASTNode
|
||||||
|
of callExpr:
|
||||||
|
callee*: ASTNode # The identifier being called
|
||||||
|
args*: tuple[positionals: seq[ASTNode], keyword: seq[ASTNode]]
|
||||||
|
# Due to how our bytecode is represented, functions can't have
|
||||||
|
# more than 255 arguments, so why bother using a full int for
|
||||||
|
# that? And plus, if your functions need more than arguments, you've
|
||||||
|
# got far bigger problems to deal with in your code, trust me.
|
||||||
|
# Oh and btw, arity is the number of arguments the function takes:
|
||||||
|
# see it as len(self.args.positionals) + len(self.args.keyword)
|
||||||
|
# (because it's what it is)
|
||||||
|
arity*: int8
|
||||||
|
of unaryExpr:
|
||||||
|
unOp*: Token
|
||||||
|
operand*: ASTNode
|
||||||
|
of binaryExpr:
|
||||||
|
binOp*: Token
|
||||||
|
a*: ASTNode
|
||||||
|
b*: ASTNode
|
||||||
|
of awaitExpr:
|
||||||
|
# The awaited object (well, in this
|
||||||
|
# case an AST node representing it)
|
||||||
|
awaitee*: ASTNode
|
||||||
|
of assignExpr:
|
||||||
|
assignName*: ASTNode
|
||||||
|
assignValue*: ASTNode
|
||||||
|
of exprStmt:
|
||||||
|
expression*: ASTNode
|
||||||
|
of importStmt:
|
||||||
|
moduleName*: ASTNode
|
||||||
|
of fromStmt:
|
||||||
|
fromModule*: ASTNode
|
||||||
|
fromAttributes*: seq[ASTNode]
|
||||||
|
of delStmt:
|
||||||
|
delName*: ASTNode
|
||||||
|
of assertStmt:
|
||||||
|
assertExpr*: ASTNode
|
||||||
|
of raiseStmt:
|
||||||
|
exception*: ASTNode
|
||||||
|
of blockStmt:
|
||||||
|
statements*: seq[ASTNode]
|
||||||
|
of whileStmt:
|
||||||
|
whileCondition*: ASTNode
|
||||||
|
loopBody*: seq[ASTNode]
|
||||||
|
of returnStmt:
|
||||||
|
retValue*: ASTNode
|
||||||
|
of ifStmt:
|
||||||
|
ifCondition*: ASTNode
|
||||||
|
thenBranch*: ASTNode
|
||||||
|
elseBranch*: ASTNode
|
||||||
|
of funDecl:
|
||||||
|
funcName*: ASTNode
|
||||||
|
funcBody*: ASTNode
|
||||||
|
arguments*: tuple[positionals: seq[ASTNode], keyword: seq[ASTNode], defaults: seq[ASTNode]]
|
||||||
|
isAsync*: bool
|
||||||
|
isGenerator*: bool
|
||||||
|
of classDecl:
|
||||||
|
className*: ASTNode
|
||||||
|
classBody*: ASTNode
|
||||||
|
parents*: seq[ASTNode]
|
||||||
|
else:
|
||||||
|
# Types such as booleans and singletons
|
||||||
|
# in general don't need any extra metadata.
|
||||||
|
# This branch is also used for extra types
|
||||||
|
# that don't have a use yet
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc newASTNode*(token: Token, kind: NodeKind, children: seq[ASTNode] = @[], pos: tuple[start, stop: int] = (-1, -1)): ASTNode =
|
proc newASTNode*(kind: NodeKind, pos: tuple[start, stop: int] = (-1, -1)): ASTNode =
|
||||||
## Initializes a new ASTNode object
|
## Initializes a new ASTNode object
|
||||||
new(result)
|
new(result)
|
||||||
result.token = token
|
|
||||||
result.kind = kind
|
result.kind = kind
|
||||||
result.children = children
|
|
||||||
result.pos = pos
|
result.pos = pos
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: ASTNode): string =
|
proc `$`*(self: ASTNode): string =
|
||||||
result &= "ASTNode("
|
result = &"ASTNode(kind={self.kind})"
|
||||||
if self.token.kind != TokenType.EndOfFile:
|
|
||||||
result &= &"token={self.token}, "
|
|
||||||
result &= &"kind={self.kind}"
|
|
||||||
if self.children.len() > 0:
|
|
||||||
result &= &", children=[{self.children.join(\", \")}]"
|
|
||||||
result &= ")"
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue