Removed capitalization from error messages. Added support for del and assert statements

This commit is contained in:
nocturn9x 2021-08-24 11:33:52 +02:00
parent 3f1c6077bc
commit d192d5e6b7
5 changed files with 69 additions and 26 deletions

View File

@ -87,6 +87,7 @@ ifStmt → "if" "(" expression ")" statement ( "else" statement )?; //
returnStmt → "return" expression? ";"; // Returns from a function, illegal in top-level code returnStmt → "return" expression? ";"; // Returns from a function, illegal in top-level code
breakStmt → "break" ";"; breakStmt → "break" ";";
assertStmt → "assert" expression ";" assertStmt → "assert" expression ";"
delStmt → "del" expression ";"
continueStmt → "continue" ";"; continueStmt → "continue" ";";
whileStmt → "while" "(" expression ")" statement; // While loops run until their condition is truthy whileStmt → "while" "(" expression ")" statement; // While loops run until their condition is truthy
blockStmt → "{" declaration* "}"; // Blocks create a new scope that lasts until they're closed blockStmt → "{" declaration* "}"; // Blocks create a new scope that lasts until they're closed

View File

@ -78,7 +78,7 @@ const reserved = to_table({
"isnot": TokenType.IsNot, "from": TokenType.From, "isnot": TokenType.IsNot, "from": TokenType.From,
"let": TokenType.Let, "const": TokenType.Const, "let": TokenType.Let, "const": TokenType.Const,
"assert": TokenType.Assert, "or": TokenType.LogicalOr, "assert": TokenType.Assert, "or": TokenType.LogicalOr,
"and": TokenType.LogicalAnd "and": TokenType.LogicalAnd, "del": TokenType.Del
}) })
type type
@ -197,10 +197,10 @@ proc match(self: Lexer, what: char): bool =
## the given character, and consumes it. ## the given character, and consumes it.
## Otherwise, false is returned ## Otherwise, false is returned
if self.done(): if self.done():
self.error("Unexpected EOF") self.error("unexpected EOF")
return false return false
elif not self.check(what): elif not self.check(what):
self.error(&"Expecting '{what}', got '{self.peek()}' instead") self.error(&"expecting '{what}', got '{self.peek()}' instead")
return false return false
self.current += 1 self.current += 1
return true return true
@ -246,7 +246,7 @@ proc parseString(self: Lexer, delimiter: char, mode: string = "single") =
if self.check('\n') and mode == "multi": if self.check('\n') and mode == "multi":
self.line = self.line + 1 self.line = self.line + 1
else: else:
self.error("Unexpected EOL while parsing string literal") self.error("unexpected EOL while parsing string literal")
return return
if mode in ["raw", "multi"]: if mode in ["raw", "multi"]:
discard self.step() discard self.step()
@ -290,14 +290,14 @@ proc parseString(self: Lexer, delimiter: char, mode: string = "single") =
of '\\': of '\\':
self.source[self.current] = cast[char](0x5C) self.source[self.current] = cast[char](0x5C)
else: else:
self.error(&"Invalid escape sequence '\\{self.peek()}'") self.error(&"invalid escape sequence '\\{self.peek()}'")
return return
if self.done(): if self.done():
self.error(&"Unexpected EOF while parsing string literal") self.error(&"inexpected EOF while parsing string literal")
return return
if mode == "multi": if mode == "multi":
if not self.match(delimiter.repeat(3)): if not self.match(delimiter.repeat(3)):
self.error("Unexpected EOL while parsing multi-line string literal") self.error("unexpected EOL while parsing multi-line string literal")
else: else:
discard self.step() discard self.step()
self.createToken(TokenType.String) self.createToken(TokenType.String)
@ -307,7 +307,7 @@ proc parseBinary(self: Lexer) =
## Parses binary numbers ## Parses binary numbers
while self.peek().isDigit(): while self.peek().isDigit():
if not self.check(['0', '1']): if not self.check(['0', '1']):
self.error(&"Invalid digit '{self.peek()}' in binary literal") self.error(&"invalid digit '{self.peek()}' in binary literal")
return return
discard self.step() discard self.step()
self.createToken(TokenType.Binary) self.createToken(TokenType.Binary)
@ -321,7 +321,7 @@ proc parseOctal(self: Lexer) =
## Parses octal numbers ## Parses octal numbers
while self.peek().isDigit(): while self.peek().isDigit():
if self.peek() notin '0'..'7': if self.peek() notin '0'..'7':
self.error(&"Invalid digit '{self.peek()}' in octal literal") self.error(&"invalid digit '{self.peek()}' in octal literal")
return return
discard self.step() discard self.step()
self.createToken(TokenType.Octal) self.createToken(TokenType.Octal)
@ -331,7 +331,7 @@ proc parseHex(self: Lexer) =
## Parses hexadecimal numbers ## Parses hexadecimal numbers
while self.peek().isAlphaNumeric(): while self.peek().isAlphaNumeric():
if not self.peek().isDigit() and self.peek().toLowerAscii() notin 'a'..'f': if not self.peek().isDigit() and self.peek().toLowerAscii() notin 'a'..'f':
self.error(&"Invalid hexadecimal literal") self.error(&"invalid hexadecimal literal")
return return
discard self.step() discard self.step()
self.createToken(TokenType.Hex) self.createToken(TokenType.Hex)
@ -373,7 +373,7 @@ proc parseNumber(self: Lexer) =
# TODO: Is there a better way? # TODO: Is there a better way?
discard self.step() discard self.step()
if not isDigit(self.peek()): if not isDigit(self.peek()):
self.error("Invalid float number literal") self.error("invalid float number literal")
return return
kind = TokenType.Float kind = TokenType.Float
while isDigit(self.peek()): while isDigit(self.peek()):
@ -429,7 +429,7 @@ proc next(self: Lexer) =
self.parseString(self.peek(-1), "bytes") self.parseString(self.peek(-1), "bytes")
else: else:
# TODO: Format strings? (f"{hello}") # TODO: Format strings? (f"{hello}")
self.error(&"Unknown string prefix '{single}'") self.error(&"unknown string prefix '{single}'")
return return
elif single.isAlphaNumeric() or single == '_': elif single.isAlphaNumeric() or single == '_':
self.parseIdentifier() self.parseIdentifier()
@ -456,7 +456,7 @@ proc next(self: Lexer) =
# Eventually we emit a single token # Eventually we emit a single token
self.createToken(tokens[single]) self.createToken(tokens[single])
else: else:
self.error(&"Unexpected token '{single}'") self.error(&"unexpected token '{single}'")
proc lex*(self: Lexer, source, file: string): seq[Token] = proc lex*(self: Lexer, source, file: string): seq[Token] =

View File

@ -40,7 +40,8 @@ type
whileStmt, whileStmt,
blockStmt, blockStmt,
raiseStmt, raiseStmt,
assertStmt assertStmt,
delStmt,
fromStmt, fromStmt,
importStmt, importStmt,
# An expression followed by a semicolon # An expression followed by a semicolon

View File

@ -35,7 +35,7 @@ type
Function, Break, Lambda, Function, Break, Lambda,
Continue, Var, Let, Const, Is, Continue, Var, Let, Const, Is,
Return, Async, Class, Import, From, Return, Async, Class, Import, From,
IsNot, Raise, Assert IsNot, Raise, Assert, Del
# Basic types # Basic types

View File

@ -20,8 +20,6 @@ import meta/ast
export token, ast export token, ast
type Parser* = ref object type Parser* = ref object
## A recursive-descent top-down ## A recursive-descent top-down
## parser implementation ## parser implementation
@ -46,6 +44,7 @@ proc initParser*(self: Parser = nil): Parser =
template endOfFile: Token = Token(kind: TokenType.EndOfFile, lexeme: "", line: -1) template endOfFile: Token = Token(kind: TokenType.EndOfFile, lexeme: "", line: -1)
template endOfLine(msg: string) = discard self.expect(TokenType.Semicolon, msg)
proc peek(self: Parser, distance: int = 0): Token = proc peek(self: Parser, distance: int = 0): Token =
@ -144,7 +143,7 @@ proc expect(self: Parser, kind: TokenType, message: string = ""): bool =
else: else:
result = false result = false
if message.len() == 0: if message.len() == 0:
self.error(&"Expecting token of kind {kind}, found {self.peek().kind} instead") self.error(&"expecting token of kind {kind}, found {self.peek().kind} instead")
else: else:
self.error(message) self.error(message)
@ -175,10 +174,10 @@ proc primary(self: Parser): ASTNode =
of TokenType.LeftParen: of TokenType.LeftParen:
discard self.step() discard self.step()
result = self.expression() result = self.expression()
if self.expect(TokenType.RightParen, "Unmatched '('"): if self.expect(TokenType.RightParen, "unmatched '('"):
result = newASTNode(self.peek(-3), NodeKind.groupingExpr, @[result]) result = newASTNode(self.peek(-3), NodeKind.groupingExpr, @[result])
of TokenType.RightParen: of TokenType.RightParen:
self.error("Unmatched ')'") self.error("unmatched ')'")
of TokenType.Hex: of TokenType.Hex:
result = newASTNode(self.step(), NodeKind.hexExpr) result = newASTNode(self.step(), NodeKind.hexExpr)
of TokenType.Octal: of TokenType.Octal:
@ -186,7 +185,7 @@ proc primary(self: Parser): ASTNode =
of TokenType.Binary: of TokenType.Binary:
result = newASTNode(self.step(), NodeKind.binExpr) result = newASTNode(self.step(), NodeKind.binExpr)
else: else:
self.error("Invalid syntax") self.error("invalid syntax")
proc make_call(self: Parser, callee: ASTNode): ASTNode = proc make_call(self: Parser, callee: ASTNode): ASTNode =
@ -196,7 +195,7 @@ proc make_call(self: Parser, callee: ASTNode): ASTNode =
if not self.check(TokenType.RightParen): if not self.check(TokenType.RightParen):
while true: while true:
if len(arguments) >= 255: if len(arguments) >= 255:
self.error("Cannot have more than 255 arguments") self.error("cannot have more than 255 arguments")
break break
arguments.add(self.expression()) arguments.add(self.expression())
if not self.match(TokenType.Comma): if not self.match(TokenType.Comma):
@ -213,7 +212,7 @@ proc call(self: Parser): ASTNode =
if self.match(TokenType.LeftParen): if self.match(TokenType.LeftParen):
result = self.make_call(result) result = self.make_call(result)
elif self.match(TokenType.Dot): elif self.match(TokenType.Dot):
if self.expect(TokenType.Identifier, "Expecting attribute name after '.'"): if self.expect(TokenType.Identifier, "expecting attribute name after '.'"):
result = newASTNode(self.peek(-2), NodeKind.getExpr, @[result, newAstNode(self.peek(-1), NodeKind.identExpr)]) result = newASTNode(self.peek(-2), NodeKind.getExpr, @[result, newAstNode(self.peek(-1), NodeKind.identExpr)])
else: else:
break break
@ -324,17 +323,59 @@ proc assignment(self: Parser): ASTNode =
proc expression(self: Parser): ASTNode = proc expression(self: Parser): ASTNode =
## Parses expressions ## Parses expressions
self.assignment() result = self.assignment()
proc expressionStatement(self: Parser): ASTNode = proc expressionStatement(self: Parser): ASTNode =
## Parses expression statements, which ## Parses expression statements, which
## are expressions followed by a semicolon ## are expressions followed by a semicolon
var expression = self.expression() var expression = self.expression()
discard self.expect(TokenType.Semicolon, "missing semicolon after expression") endOfLIne("missing semicolon after expression")
result = newAstNode(self.peek(-1), NodeKind.exprStmt, @[expression]) result = newAstNode(self.peek(-1), NodeKind.exprStmt, @[expression])
proc delStmt(self: Parser): ASTNode =
## Parses "del" statements,
## which unbind a name from its
## value in the current scope and
## calls its destructor
var expression = self.expression()
endOfLIne("missing semicolon after del statement")
if expression.kind != NodeKind.identExpr:
self.error("cannot delete a literal")
else:
result = newASTNode(self.peek(-1), NodeKind.delStmt, @[expression])
proc assertStmt(self: Parser): ASTNode =
## Parses "assert" statements,
## raise an error if the expression
## fed into them is falsey
var expression = self.expression()
endOfLIne("missing semicolon after del statement")
result = newASTNode(self.peek(), NodeKind.assertStmt, @[expression])
proc statement(self: Parser): ASTNode =
## Parses statement
# TODO
case self.peek().kind:
of TokenType.Del:
discard self.step()
result = self.delStmt()
of TokenType.Assert:
discard self.step()
result = self.assertStmt()
else:
result = self.expressionStatement()
proc declaration(self: Parser): ASTNode =
## Parses declarations
# TODO
result = self.statement()
proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] = proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
## Parses a series of tokens into an AST node ## Parses a series of tokens into an AST node
discard self.initParser() discard self.initParser()
@ -342,7 +383,7 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
self.file = file self.file = file
var program: seq[ASTNode] = @[] var program: seq[ASTNode] = @[]
while not self.done(): while not self.done():
program.add(self.expressionStatement()) program.add(self.declaration())
if self.errored: if self.errored:
program = @[] program = @[]
break break