|
|
|
@ -24,7 +24,7 @@ import ast
|
|
|
|
|
import token
|
|
|
|
|
import errors
|
|
|
|
|
import config
|
|
|
|
|
import lexer as l
|
|
|
|
|
import lexer
|
|
|
|
|
import util/symbols
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -79,14 +79,14 @@ type
|
|
|
|
|
# to either a FunDecl or LambdaExpr
|
|
|
|
|
# AST node and is nil when the parser
|
|
|
|
|
# is at the top-level. It allows the
|
|
|
|
|
# parser to detect errors like return
|
|
|
|
|
# outside functions
|
|
|
|
|
# parser to detect errors like using
|
|
|
|
|
# return outside functions
|
|
|
|
|
currentFunction: Declaration
|
|
|
|
|
# Stores the current scope depth (0 = global, > 0 local)
|
|
|
|
|
scopeDepth: int
|
|
|
|
|
# Operator table
|
|
|
|
|
operators: OperatorTable
|
|
|
|
|
# The AST node
|
|
|
|
|
# The AST we're producing
|
|
|
|
|
tree: seq[Declaration]
|
|
|
|
|
# Stores line data
|
|
|
|
|
lines: seq[tuple[start, stop: int]]
|
|
|
|
@ -99,6 +99,7 @@ type
|
|
|
|
|
# to avoid importing a module twice and to
|
|
|
|
|
# detect recursive dependency cycles
|
|
|
|
|
modules: TableRef[string, bool]
|
|
|
|
|
|
|
|
|
|
ParseError* = ref object of PeonException
|
|
|
|
|
## A parsing exception
|
|
|
|
|
parser*: Parser
|
|
|
|
@ -203,9 +204,8 @@ proc done(self: Parser): bool {.inline.} =
|
|
|
|
|
result = self.peek().kind == EndOfFile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc step(self: Parser, n: int = 1): Token {.inline.} =
|
|
|
|
|
## Steps n tokens into the input,
|
|
|
|
|
## returning the last consumed one
|
|
|
|
|
proc step(self: Parser): Token {.inline.} =
|
|
|
|
|
## Consumes a token and returns it
|
|
|
|
|
if self.done():
|
|
|
|
|
result = self.peek()
|
|
|
|
|
else:
|
|
|
|
@ -228,8 +228,7 @@ proc error(self: Parser, message: string, token: Token = nil) {.raises: [ParseEr
|
|
|
|
|
# tell at tokenization time which of the two contexts we're in, we just treat everything
|
|
|
|
|
# as a symbol and in the cases where we need a specific token we just match the string
|
|
|
|
|
# directly
|
|
|
|
|
proc check[T: TokenType or string](self: Parser, kind: T,
|
|
|
|
|
distance: int = 0): bool {.inline.} =
|
|
|
|
|
proc check[T: TokenType or string](self: Parser, kind: T, distance: int = 0): bool {.inline.} =
|
|
|
|
|
## Checks if the given token at the given distance
|
|
|
|
|
## matches the expected kind and returns a boolean.
|
|
|
|
|
## The distance parameter is passed directly to
|
|
|
|
@ -302,11 +301,9 @@ proc expect[T: TokenType or string](self: Parser, kind: openarray[T], message: s
|
|
|
|
|
proc expression(self: Parser): Expression
|
|
|
|
|
proc expressionStatement(self: Parser): Statement
|
|
|
|
|
proc statement(self: Parser): Statement
|
|
|
|
|
proc varDecl(self: Parser, isLet: bool = false,
|
|
|
|
|
isConst: bool = false): Declaration
|
|
|
|
|
proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declaration
|
|
|
|
|
proc parseFunExpr(self: Parser): LambdaExpr
|
|
|
|
|
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|
|
|
|
isLambda: bool = false, isOperator: bool = false): Declaration
|
|
|
|
|
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): Declaration
|
|
|
|
|
proc declaration(self: Parser): Declaration
|
|
|
|
|
proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[start, stop: int]], source: string, persist: bool = false): seq[Declaration]
|
|
|
|
|
proc findOperators(self: Parser, tokens: seq[Token])
|
|
|
|
@ -416,7 +413,6 @@ proc makeCall(self: Parser, callee: Expression): CallExpr =
|
|
|
|
|
while true:
|
|
|
|
|
if argCount >= 255:
|
|
|
|
|
self.error("can not pass more than 255 arguments in function call")
|
|
|
|
|
break
|
|
|
|
|
argument = self.expression()
|
|
|
|
|
if argument.kind == assignExpr:
|
|
|
|
|
var assign = AssignExpr(argument)
|
|
|
|
@ -547,7 +543,7 @@ proc parseAnd(self: Parser): Expression =
|
|
|
|
|
result = self.parseCmp()
|
|
|
|
|
var operator: Token
|
|
|
|
|
var right: Expression
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == Precedence.And:
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == And:
|
|
|
|
|
operator = self.step()
|
|
|
|
|
right = self.parseCmp()
|
|
|
|
|
result = newBinaryExpr(result, operator, right)
|
|
|
|
@ -559,7 +555,7 @@ proc parseOr(self: Parser): Expression =
|
|
|
|
|
result = self.parseAnd()
|
|
|
|
|
var operator: Token
|
|
|
|
|
var right: Expression
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == Precedence.Or:
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == Or:
|
|
|
|
|
operator = self.step()
|
|
|
|
|
right = self.parseAnd()
|
|
|
|
|
result = newBinaryExpr(result, operator, right)
|
|
|
|
@ -588,7 +584,7 @@ proc parseArrow(self: Parser): Expression =
|
|
|
|
|
result = self.parseAssign()
|
|
|
|
|
var operator: Token
|
|
|
|
|
var right: Expression
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == Precedence.Arrow:
|
|
|
|
|
while self.check([Identifier, Symbol]) and self.operators.getPrecedence(self.peek().lexeme) == Arrow:
|
|
|
|
|
operator = self.step()
|
|
|
|
|
right = self.parseAssign()
|
|
|
|
|
result = newBinaryExpr(result, operator, right)
|
|
|
|
@ -815,7 +811,7 @@ proc importStmt(self: Parser, fromStmt: bool = false): Statement =
|
|
|
|
|
elif i == searchPath.high():
|
|
|
|
|
self.error(&"""could not import '{path}': module not found""")
|
|
|
|
|
if not self.modules.getOrDefault(path, true):
|
|
|
|
|
self.error(&"coult not import '{path}' (recursive dependency detected)")
|
|
|
|
|
self.error(&"coult not import '{path}' from '{self.file}' due to a cyclic dependency")
|
|
|
|
|
else:
|
|
|
|
|
self.modules[path] = false
|
|
|
|
|
try:
|
|
|
|
@ -940,8 +936,7 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
|
|
|
|
elif self.match("("):
|
|
|
|
|
while not self.match(")") and not self.done():
|
|
|
|
|
exp = self.primary()
|
|
|
|
|
if exp.kind notin {strExpr, intExpr, octExpr, binExpr, hexExpr, floatExpr,
|
|
|
|
|
trueExpr, falseExpr}:
|
|
|
|
|
if not exp.isConst():
|
|
|
|
|
self.error("pragma arguments can only be literals", exp.token)
|
|
|
|
|
args.add(LiteralExpr(exp))
|
|
|
|
|
if not self.match(","):
|
|
|
|
@ -949,8 +944,7 @@ proc parsePragmas(self: Parser): seq[Pragma] =
|
|
|
|
|
self.expect(LeftParen, "unterminated parenthesis in pragma arguments")
|
|
|
|
|
else:
|
|
|
|
|
exp = self.primary()
|
|
|
|
|
if exp.kind notin {strExpr, intExpr, octExpr, binExpr, hexExpr, floatExpr,
|
|
|
|
|
trueExpr, falseExpr}:
|
|
|
|
|
if not exp.isConst():
|
|
|
|
|
self.error("pragma arguments can only be literals", exp.token)
|
|
|
|
|
args.add(LiteralExpr(exp))
|
|
|
|
|
result.add(newPragma(name, args))
|
|
|
|
@ -990,14 +984,11 @@ proc varDecl(self: Parser, isLet: bool = false,
|
|
|
|
|
pragmas.add(pragma)
|
|
|
|
|
case tok.kind:
|
|
|
|
|
of TokenType.Var:
|
|
|
|
|
result = newVarDecl(name, value, isPrivate = isPrivate, token = tok,
|
|
|
|
|
valueType = valueType, pragmas = (@[]))
|
|
|
|
|
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, valueType=valueType, pragmas=(@[]))
|
|
|
|
|
of Const:
|
|
|
|
|
result = newVarDecl(name, value, isPrivate = isPrivate, token = tok,
|
|
|
|
|
isConst = true, valueType = valueType, pragmas = (@[]))
|
|
|
|
|
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, valueType = valueType, pragmas = (@[]))
|
|
|
|
|
result = newVarDecl(name, value, isPrivate=isPrivate, token=tok, isLet=isLet, valueType=valueType, pragmas=(@[]))
|
|
|
|
|
else:
|
|
|
|
|
discard # Unreachable
|
|
|
|
|
if not hasInit and VarDecl(result).valueType.isNil():
|
|
|
|
@ -1064,7 +1055,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
|
|
|
|
|
proc parseGenericConstraint(self: Parser): Expression =
|
|
|
|
|
## Recursively parses a generic constraint
|
|
|
|
|
## and returns it as an expression
|
|
|
|
|
result = self.expression() # First value is always an identifier of some sort
|
|
|
|
|
result = self.expression()
|
|
|
|
|
if not self.check(RightBracket):
|
|
|
|
|
case self.peek().lexeme:
|
|
|
|
|
of "|":
|
|
|
|
@ -1139,8 +1130,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|
|
|
|
elif isLambda:
|
|
|
|
|
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok,
|
|
|
|
|
returnType=nil, depth=self.scopeDepth)
|
|
|
|
|
if self.match(LeftParen):
|
|
|
|
|
var parameter: tuple[name: IdentExpr, valueType: Expression]
|
|
|
|
|
self.parseDeclArguments(arguments, parameter, defaults)
|
|
|
|
|
if self.match(":"):
|
|
|
|
|
# Function has explicit return type
|
|
|
|
|
# Function returns a value
|
|
|
|
|
if self.match([Function, Coroutine, Generator]):
|
|
|
|
|
# The function's return type is another
|
|
|
|
|
# function. We specialize this case because
|
|
|
|
@ -1150,15 +1144,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|
|
|
|
returnType = self.parseFunExpr()
|
|
|
|
|
else:
|
|
|
|
|
returnType = self.expression()
|
|
|
|
|
if self.match(LeftParen):
|
|
|
|
|
var parameter: tuple[name: IdentExpr, valueType: Expression]
|
|
|
|
|
self.parseDeclArguments(arguments, parameter, defaults)
|
|
|
|
|
if self.match(":"):
|
|
|
|
|
# Function's return type
|
|
|
|
|
if self.match([Function, Coroutine, Generator]):
|
|
|
|
|
returnType = self.parseFunExpr()
|
|
|
|
|
else:
|
|
|
|
|
returnType = self.expression()
|
|
|
|
|
if self.currentFunction.kind == funDecl:
|
|
|
|
|
if not self.match(Semicolon):
|
|
|
|
|
# If we don't find a semicolon,
|
|
|
|
|