Parser support for forward declarations
This commit is contained in:
parent
a86a1736ec
commit
79af16ccc9
|
@ -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?")
|
||||||
|
|
|
@ -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
|
||||||
|
|
30
src/test.nim
30
src/test.nim
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue