Parser support for forward declarations

This commit is contained in:
Nocturn9x 2022-04-07 12:15:34 +02:00
parent a86a1736ec
commit 79af16ccc9
3 changed files with 43 additions and 28 deletions

View File

@ -1006,12 +1006,6 @@ proc funDecl(self: Compiler, node: FunDecl) =
self.currentFunction = function self.currentFunction = function
proc classDecl(self: Compiler, node: ClassDecl) =
## Compiles class declarations
self.declareName(node.name)
self.emitByte(MakeClass)
self.blockStmt(BlockStmt(node.body))
proc declaration(self: Compiler, node: ASTNode) = proc declaration(self: Compiler, node: ASTNode) =
## Compiles all declarations ## Compiles all declarations
@ -1020,8 +1014,6 @@ proc declaration(self: Compiler, node: ASTNode) =
self.varDecl(VarDecl(node)) self.varDecl(VarDecl(node))
of NodeKind.funDecl: of NodeKind.funDecl:
self.funDecl(FunDecl(node)) self.funDecl(FunDecl(node))
of NodeKind.classDecl:
self.classDecl(ClassDecl(node))
else: else:
self.statement(node) self.statement(node)
@ -1045,4 +1037,4 @@ proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk =
self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope
result = self.chunk result = self.chunk
if self.ast.len() > 0 and self.scopeDepth != -1: if self.ast.len() > 0 and self.scopeDepth != -1:
self.error(&"internal error: invalid scopeDepth state (expected -1, got {self.scopeDepth}), did you forget to call endScope/beginScope?") self.error(&"invalid state: invalid scopeDepth value (expected -1, got {self.scopeDepth}), did you forget to call endScope/beginScope?")

View File

@ -77,8 +77,12 @@ proc newParser*(): Parser =
result.scopeDepth = 0 result.scopeDepth = 0
# Public getters for improved error formatting # Public getters for improved error formatting
proc getCurrent*(self: Parser): int = self.current proc getCurrent*(self: Parser): int {.inline.} = self.current
proc getCurrentToken*(self: Parser): Token = (if self.getCurrent() >= self.tokens.len(): self.tokens[^1] else: self.tokens[self.current - 1]) proc getCurrentToken*(self: Parser): Token =
if self.getCurrent() >= self.tokens.high() or self.getCurrent() - 1 < 0:
return self.tokens[^1]
else:
return self.tokens[self.current - 1]
# Handy templates to make our life easier, thanks nim! # Handy templates to make our life easier, thanks nim!
template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1) template endOfFile: Token = Token(kind: EndOfFile, lexeme: "", line: -1)
@ -190,7 +194,8 @@ proc declaration(self: Parser): ASTNode
proc primary(self: Parser): ASTNode = proc primary(self: Parser): ASTNode =
## Parses primary expressions such ## Parses primary expressions such
## as integer literals and keywords ## as integer literals and keywords
## that map to builtin types (true, false, etc) ## that map to builtin types (true,
## false, nil, etc.)
case self.peek().kind: case self.peek().kind:
of True: of True:
result = newTrueExpr(self.step()) result = newTrueExpr(self.step())
@ -326,7 +331,7 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
if not self.check(RightParen): if not self.check(RightParen):
while true: while true:
if argCount >= 255: if argCount >= 255:
self.error("cannot store more than 255 arguments") self.error("call can not have more than 255 arguments")
break break
argument = self.expression() argument = self.expression()
if argument.kind == assignExpr: if argument.kind == assignExpr:
@ -337,7 +342,7 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
elif arguments.keyword.len() == 0: elif arguments.keyword.len() == 0:
arguments.positionals.add(argument) arguments.positionals.add(argument)
else: else:
self.error("positional argument(s) cannot follow keyword argument(s) in call") self.error("positional argument cannot follow keyword argument in call")
if not self.match(Comma): if not self.match(Comma):
break break
argCount += 1 argCount += 1
@ -879,7 +884,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
return result return result
elif isLambda: elif isLambda:
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok) self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok)
else: elif not isOperator:
self.error("funDecl: invalid state") self.error("funDecl: invalid state")
if self.match(Colon): if self.match(Colon):
# A function without an explicit # A function without an explicit
@ -913,11 +918,17 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
# Function's return type # Function's return type
self.expect(Identifier, "expecting return type after ':'") self.expect(Identifier, "expecting return type after ':'")
returnType = newIdentExpr(self.peek(-1)) returnType = newIdentExpr(self.peek(-1))
self.expect(LeftBrace)
if self.currentFunction.kind == funDecl: if self.currentFunction.kind == funDecl:
if not self.match(Semicolon): if not self.match(Semicolon):
# Forward declaration! # If we don't find a semicolon,
# it's not a forward declaration
self.expect(LeftBrace)
FunDecl(self.currentFunction).body = self.blockStmt() FunDecl(self.currentFunction).body = self.blockStmt()
else:
# This is a forward declaration so we explicitly
# nullify the function's body to tell the compiler
# to look for it elsewhere in the file later
FunDecl(self.currentFunction).body = nil
FunDecl(self.currentFunction).arguments = arguments FunDecl(self.currentFunction).arguments = arguments
FunDecl(self.currentFunction).returnType = returnType FunDecl(self.currentFunction).returnType = returnType
else: else:
@ -934,6 +945,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
self.error("cannot declare argument-less operator") self.error("cannot declare argument-less operator")
elif arguments.len() > 2: elif arguments.len() > 2:
self.error("cannot declare operator with more than 2 arguments") self.error("cannot declare operator with more than 2 arguments")
elif FunDecl(result).returnType == nil:
self.error("operator cannot have void return type")
self.currentFunction = enclosingFunction self.currentFunction = enclosingFunction
@ -1047,7 +1060,7 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
# ending the input with an EOF token # ending the input with an EOF token
if token.kind == Operator: if token.kind == Operator:
if i == self.tokens.high(): if i == self.tokens.high():
self.error("invalid state: found malformed tokenizer input while looking for operators") self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)")
elif self.tokens[i + 1].kind != Identifier: elif self.tokens[i + 1].kind != Identifier:
# The input is probably malformed, # The input is probably malformed,
# we'll catch this error later # we'll catch this error later

View File

@ -21,10 +21,15 @@ proc fillSymbolTable(tokenizer: Lexer)
proc getLineEditor: LineEditor proc getLineEditor: LineEditor
const debugLexer = false
const debugParser = true
when isMainModule: when isMainModule:
setControlCHook(proc () {.noconv.} = quit(0)) setControlCHook(proc () {.noconv.} = quit(0))
var keep = true var keep = true
var tokens: seq[Token] = @[] var tokens: seq[Token] = @[]
var tree: seq[ASTNode] = @[]
var tokenizer = newLexer() var tokenizer = newLexer()
var parser = newParser() var parser = newParser()
let editor = getLineEditor() let editor = getLineEditor()
@ -42,18 +47,23 @@ when isMainModule:
if input.len() > 0: if input.len() > 0:
# Currently the parser doesn't handle these tokens well # Currently the parser doesn't handle these tokens well
tokens = filter(tokenizer.lex(input, "<stdin>"), proc (x: Token): bool = x.kind notin {TokenType.Whitespace, Tab}) tokens = filter(tokenizer.lex(input, "<stdin>"), proc (x: Token): bool = x.kind notin {TokenType.Whitespace, Tab})
echo "Tokenization step:" when debugLexer:
for i, token in tokens: echo "Tokenization step:"
if i == tokens.high(): for i, token in tokens:
# Who cares about EOF? if i == tokens.high():
break # Who cares about EOF?
echo "\t", token break
echo "" echo "\t", token
echo "Parsing step:" echo ""
for node in parser.parse(tokens, "<stdin>"): tree = parser.parse(tokens, "<stdin>")
echo "\t", node when debugParser:
echo "Parsing step:"
for node in tree:
echo "\t", node
except IOError: except IOError:
break break
# TODO: The code for error reporting completely
# breaks down with multiline input, fix it
except LexingError: except LexingError:
let lineNo = tokenizer.getLine() let lineNo = tokenizer.getLine()
let relPos = tokenizer.getRelPos(lineNo) let relPos = tokenizer.getRelPos(lineNo)