Wired up the parser

This commit is contained in:
Nocturn9x 2022-04-05 11:23:59 +02:00
parent 5ea6f91ce4
commit c91cbf0ac1
2 changed files with 74 additions and 161 deletions

View File

@ -74,7 +74,7 @@ type
scopeDepth: int
proc initParser*(): Parser =
proc newParser*(): Parser =
## Initializes a new Parser object
new(result)
result.current = 0
@ -327,7 +327,7 @@ proc primary(self: Parser): ASTNode =
proc makeCall(self: Parser, callee: ASTNode): ASTNode =
## Utility function called iteratively by self.call()
## to parse a function-like call
## to parse a function call
let tok = self.peek(-1)
var argNames: seq[ASTNode] = @[]
var arguments: tuple[positionals: seq[ASTNode], keyword: seq[tuple[name: ASTNode, value: ASTNode]]] = (positionals: @[], keyword: @[])
@ -347,7 +347,7 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
elif arguments.keyword.len() == 0:
arguments.positionals.add(argument)
else:
self.error("positional arguments cannot follow keyword arguments in call")
self.error("positional argument(s) cannot follow keyword argument(s) in call")
if not self.match(Comma):
break
argCount += 1
@ -392,7 +392,7 @@ proc pow(self: Parser): ASTNode =
result = self.unary()
var operator: Token
var right: ASTNode
while self.match(DoubleAsterisk):
while self.match(DoubleStar):
operator = self.peek(-1)
right = self.unary()
result = newBinaryExpr(result, operator, right)
@ -403,7 +403,7 @@ proc mul(self: Parser): ASTNode =
result = self.pow()
var operator: Token
var right: ASTNode
while self.match([Slash, Percentage, FloorDiv, Asterisk]):
while self.match([Slash, Percentage, FloorDiv, Star]):
operator = self.peek(-1)
right = self.pow()
result = newBinaryExpr(result, operator, right)
@ -421,7 +421,8 @@ proc add(self: Parser): ASTNode =
proc comparison(self: Parser): ASTNode =
## Parses comparison expressions
## Parses other comparison expressions
## and some other operators
result = self.add()
var operator: Token
var right: ASTNode
@ -443,7 +444,8 @@ proc equality(self: Parser): ASTNode =
proc logicalAnd(self: Parser): ASTNode =
## Parses logical AND expressions
## Parses logical and expressions
## (a and b)
result = self.equality()
var operator: Token
var right: ASTNode
@ -454,7 +456,8 @@ proc logicalAnd(self: Parser): ASTNode =
proc logicalOr(self: Parser): ASTNode =
## Parses logical OR expressions
## Parses logical or expressions
## (a or b)
result = self.logicalAnd()
var operator: Token
var right: ASTNode
@ -465,7 +468,7 @@ proc logicalOr(self: Parser): ASTNode =
proc bitwiseAnd(self: Parser): ASTNode =
## Parser a & b expressions
## Parses a & b expressions
result = self.logicalOr()
var operator: Token
var right: ASTNode
@ -476,7 +479,7 @@ proc bitwiseAnd(self: Parser): ASTNode =
proc bitwiseOr(self: Parser): ASTNode =
## Parser a | b expressions
## Parses a | b expressions
result = self.bitwiseAnd()
var operator: Token
var right: ASTNode
@ -504,30 +507,6 @@ proc assignment(self: Parser): ASTNode =
self.error("invalid assignment target")
proc delStmt(self: Parser): ASTNode =
## Parses "del" statements,
## which unbind a name from its
## value in the current scope and
## calls its destructor
let tok = self.peek(-1)
var expression = self.expression()
var temp = expression
endOfLIne("missing semicolon after del statement")
if expression.kind == groupingExpr:
# We unpack grouping expressions
temp = self.unnest(temp)
if temp.isLiteral():
self.error("cannot delete a literal")
elif temp.kind in {binaryExpr, unaryExpr}:
self.error("cannot delete operator")
elif temp.kind == callExpr:
self.error("cannot delete function call")
elif temp.kind == assignExpr:
self.error("cannot delete assignment")
else:
result = newDelStmt(expression, tok)
proc assertStmt(self: Parser): ASTNode =
## Parses "assert" statements,
## raise an error if the expression
@ -669,25 +648,6 @@ proc importStmt(self: Parser): ASTNode =
endOfLine("missing semicolon after import statement")
proc fromStmt(self: Parser): ASTNode =
## Parser from xx import yy statements
let tok = self.peek(-1)
self.expect(Identifier, "expecting module name(s) after import statement")
result = newIdentExpr(self.peek(-1))
var attributes: seq[ASTNode] = @[]
var attribute: ASTNode
self.expect(Import)
self.expect(Identifier)
attribute = newIdentExpr(self.peek(-1))
attributes.add(attribute)
while self.match(Comma):
self.expect(Identifier)
attribute = newIdentExpr(self.peek(-1))
attributes.add(attribute)
# from x import a [, b, c, ...];
endOfLine("missing semicolon after import statement")
result = newFromImportStmt(result, attributes, tok)
proc tryStmt(self: Parser): ASTNode =
## Parses try/except/finally/else blocks
@ -769,21 +729,6 @@ proc forStmt(self: Parser): ASTNode =
# messages
if self.match(Semicolon):
discard
elif self.match(Dynamic):
self.error("dynamic declarations are not allowed in the foor loop initializer")
elif self.match(Public):
self.error("public declarations are not allowed in the for loop initializer")
elif self.match(Static):
self.expect(Var, "expecting 'var' after 'static' in for loop initializer")
initializer = self.varDecl(isStatic=true, isPrivate=true)
elif self.match(Private):
if self.match(Dynamic):
self.error("dynamic declarations are not allowed in the foor loop initializer")
elif self.match(Static):
self.expect(Var, "expecting 'var' after 'static' in for loop initializer")
initializer = self.varDecl(isStatic=true, isPrivate=true)
elif self.match(Var):
initializer = self.varDecl(isStatic=true, isPrivate=true)
elif self.match(Var):
initializer = self.varDecl(isStatic=true, isPrivate=true)
else:
@ -933,23 +878,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
self.currentFunction = enclosingFunction
proc classDecl(self: Parser, isStatic: bool = true, isPrivate: bool = true): ASTNode =
## Parses class declarations
self.checkDecl(isStatic, isPrivate)
let tok = self.peek(-1)
var parents: seq[ASTNode] = @[]
self.expect(Identifier)
var name = newIdentExpr(self.peek(-1))
if self.match(LessThan):
while true:
self.expect(Identifier)
parents.add(newIdentExpr(self.peek(-1)))
if not self.match(Comma):
break
self.expect(LeftBrace)
result = newClassDecl(name, self.blockStmt(), isPrivate=isPrivate, isStatic=isStatic, parents=parents, token=tok, owner=self.file, closedOver=false)
proc expression(self: Parser): ASTNode =
## Parses expressions
result = self.assignment()
@ -969,9 +897,6 @@ proc statement(self: Parser): ASTNode =
of If:
discard self.step()
result = self.ifStmt()
of Del:
discard self.step()
result = self.delStmt()
of Assert:
discard self.step()
result = self.assertStmt()
@ -990,9 +915,6 @@ proc statement(self: Parser): ASTNode =
of Import:
discard self.step()
result = self.importStmt()
of From:
discard self.step()
result = self.fromStmt()
of While:
discard self.step()
result = self.whileStmt()
@ -1024,62 +946,14 @@ proc statement(self: Parser): ASTNode =
proc declaration(self: Parser): ASTNode =
## Parses declarations
case self.peek().kind:
of Var, Const:
of Var, Const, Let:
discard self.step()
result = self.varDecl()
of Class:
discard self.step()
result = self.classDecl()
of Fun:
of Function:
discard self.step()
result = self.funDecl()
of Private, Public:
discard self.step()
var isStatic: bool = true
let isPrivate = if self.peek(-1).kind == Private: true else: false
if self.match(Dynamic):
isStatic = false
elif self.match(Static):
discard # This is just to allow an "explicit" static keyword
if self.match(Async):
result = self.funDecl(isStatic=isStatic, isPrivate=isPrivate, isAsync=true)
else:
case self.peek().kind:
of Var, Const:
discard self.step()
result = self.varDecl(isStatic=isStatic, isPrivate=isPrivate)
of Class:
discard self.step()
result = self.classDecl(isStatic=isStatic, isPrivate=isPrivate)
of Fun:
discard self.step()
result = self.funDecl(isStatic=isStatic, isPrivate=isPrivate)
else:
self.error("expecting declaration")
of Static, Dynamic:
discard self.step()
let isStatic: bool = if self.peek(-1).kind == Static: true else: false
if self.match(Async):
self.expect(Fun)
result = self.funDecl(isStatic=isStatic, isPrivate=true, isAsync=true)
else:
case self.peek().kind:
of Var, Const:
discard self.step()
result = self.varDecl(isStatic=isStatic, isPrivate=true)
of Class:
discard self.step()
result = self.classDecl(isStatic=isStatic, isPrivate=true)
of Fun:
discard self.step()
result = self.funDecl(isStatic=isStatic, isPrivate=true)
else:
self.error("expecting declaration")
of Async:
discard self.step()
self.expect(Fun)
result = self.funDecl(isAsync=true)
of Type:
discard # TODO
else:
result = self.statement()

View File

@ -1,4 +1,61 @@
import sequtils
import frontend/lexer
import frontend/parser as p
import jale/editor
import jale/templates
import jale/plugin/defaults
import jale/plugin/editor_history
import jale/keycodes
import jale/multiline
proc fillSymbolTable(tokenizer: Lexer)
proc getLineEditor: LineEditor =
result = newLineEditor()
result.prompt = "=> "
result.populateDefaults() # Setup default keybindings
let hist = result.plugHistory() # Create history object
result.bindHistory(hist) # Set default history keybindings
when isMainModule:
setControlCHook(proc () {.noconv.} = quit(0))
var keep = true
var tokens: seq[Token] = @[]
var tokenizer = newLexer()
var parser = newParser()
let lineEditor = getLineEditor()
var input: string
lineEditor.bindEvent(jeQuit):
keep = false
lineEditor.bindKey("ctrl+a"):
lineEditor.content.home()
lineEditor.bindKey("ctrl+e"):
lineEditor.content.`end`()
tokenizer.fillSymbolTable()
while keep:
try:
input = lineEditor.read()
if input.len() > 0:
tokens = filter(tokenizer.lex(input, "<stdin>"), proc (x: Token): bool = x.kind notin {Whitespace, Tab})
for i, token in tokens:
if i == tokens.high():
# Who cares about EOF?
break
echo token
for node in parser.parse(tokens, "<stdin>"):
echo node
except IOError:
break
except LexingError:
echo getCurrentExceptionMsg()
except ParseError:
echo getCurrentExceptionMsg()
quit(0)
proc fillSymbolTable(tokenizer: Lexer) =
@ -106,21 +163,3 @@ proc fillSymbolTable(tokenizer: Lexer) =
# intrernally). You can add/remove symbols (and keywords
# for that matter) as you like!
when isMainModule:
setControlCHook(proc () {.noconv.} = quit(0))
var tokenizer = newLexer()
tokenizer.fillSymbolTable()
while true:
try:
stdout.write("> ")
for token in tokenizer.lex(stdin.readLine(), "<stdin>"):
if token.kind notin [Whitespace, Tab]:
# Reduces clutter in the output
echo token
except IOError:
break
except LexingError:
echo getCurrentExceptionMsg()
echo ""
quit(0)