Removed capitalization from error messages. Added support for del and assert statements
This commit is contained in:
parent
3f1c6077bc
commit
d192d5e6b7
|
@ -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
|
||||||
|
|
|
@ -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] =
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue