Improve errors raising by parser

This commit is contained in:
Bartłomiej Pluta
2019-07-06 18:04:05 +02:00
parent 6fd49ba54a
commit fbb3f79731
22 changed files with 125 additions and 118 deletions

View File

@@ -1,6 +1,7 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException
from smnp.token.type import TokenType
@@ -50,5 +51,6 @@ class AccessNode(ExpressionNode):
return Parser.oneOf(
IdentifierNode._literalParser(),
IdentifierNode._functionCallParser()
IdentifierNode._functionCallParser(),
exception=lambda input: SyntaxException(f"Expected property name or method call, found '{input.current().rawValue}'", input.currentPos())
)

View File

@@ -14,7 +14,7 @@ class BlockNode(StatementNode):
return Parser.loop(
Parser.terminalParser(TokenType.OPEN_BRACKET),
StatementNode.parse,
Parser.doAssert(StatementNode.parse, f"statement or '{TokenType.CLOSE_BRACKET.key}'"),
Parser.terminalParser(TokenType.CLOSE_BRACKET),
createNode=createNode
createNode=createNode,
)(input)

View File

@@ -44,7 +44,7 @@ class ExpressionNode(Node):
return Parser.allOf(
cls._expressionParser(),
Parser.terminalParser(TokenType.ASTERISK),
StatementNode.parse,
Parser.doAssert(StatementNode.parse, 'statement'),
createNode=createNode
)

View File

@@ -48,10 +48,10 @@ class ExtendNode(StatementNode):
return Parser.allOf(
Parser.terminalParser(TokenType.EXTEND),
TypeNode.parse,
Parser.terminalParser(TokenType.AS),
IdentifierNode.identifierParser(),
cls._methodsDeclarationsParser(),
Parser.doAssert(TypeNode.parse, "type being extended"),
Parser.terminalParser(TokenType.AS, doAssert=True),
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
Parser.doAssert(cls._methodsDeclarationsParser(), "methods declarations"),
createNode=createNode
)(input)
@@ -64,7 +64,7 @@ class ExtendNode(StatementNode):
return Parser.loop(
Parser.terminalParser(TokenType.OPEN_BRACKET),
FunctionDefinitionNode.parse,
Parser.doAssert(FunctionDefinitionNode.parse, f"method declaration or '{TokenType.CLOSE_BRACKET.key}'"),
Parser.terminalParser(TokenType.CLOSE_BRACKET),
createNode=createNode
)

View File

@@ -56,9 +56,9 @@ class FunctionDefinitionNode(StatementNode):
return Parser.allOf(
Parser.terminalParser(TokenType.FUNCTION),
IdentifierNode.identifierParser(),
cls._argumentsDeclarationParser(),
BlockNode.parse,
Parser.doAssert(IdentifierNode.identifierParser(), "function name"),
Parser.doAssert(cls._argumentsDeclarationParser(), "arguments list"),
Parser.doAssert(BlockNode.parse, "function body"),
createNode=createNode
)(input)

View File

@@ -30,7 +30,7 @@ class IdentifierNode(AccessNode):
return Parser.allOf(
IdentifierNode.identifierParser(),
Parser.terminalParser(TokenType.ASSIGN),
ExpressionNode.parse,
Parser.doAssert(ExpressionNode.parse, "expression"),
createNode=createNode
)

View File

@@ -55,10 +55,10 @@ class ImportNode(Node):
return Parser.allOf(
Parser.terminalParser(TokenType.IMPORT),
TypeNode.parse,
Parser.terminalParser(TokenType.FROM),
StringLiteralNode._literalParser(),
Parser.terminalParser(TokenType.AS),
IdentifierNode.identifierParser(),
Parser.doAssert(Parser.terminalParser(TokenType.FROM), "'from <source> as <variable name>'"),
Parser.doAssert(StringLiteralNode._literalParser(), "source as a string"),
Parser.doAssert(Parser.terminalParser(TokenType.AS), "'as <variable name>'"),
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
createNode=createNode
)
@@ -71,6 +71,6 @@ class ImportNode(Node):
return Parser.allOf(
Parser.terminalParser(TokenType.IMPORT),
StringLiteralNode._literalParser(),
Parser.doAssert(StringLiteralNode._literalParser(), "source as a string"),
createNode=createNode
)

View File

@@ -22,7 +22,7 @@ class IntegerLiteralNode(AccessNode):
return Parser.allOf(
Parser.terminalParser(TokenType.MINUS),
cls._positiveIntegerParser(),
Parser.doAssert(cls._positiveIntegerParser(), "integer"),
createNode=createNode
)

View File

@@ -2,6 +2,7 @@ from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
@@ -9,7 +10,8 @@ class ArgumentsListNode(Node):
@classmethod
def _parse(cls, input):
return abstractIterableParser(ArgumentsListNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ExpressionNode.parse)(input)
return abstractIterableParser(ArgumentsListNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN,
Parser.doAssert(ExpressionNode.parse, "expression"))(input)
class FunctionCall(AccessNode):

View File

@@ -41,7 +41,7 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
return node
return Parser.allOf(
Parser.terminalParser(TokenType.COMMA),
Parser.terminalParser(TokenType.COMMA, doAssert=True),
itemParser,
AbstractIterableTailNode.parse,
createNode=createNode

View File

@@ -1,6 +1,7 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
@@ -8,4 +9,5 @@ class ListNode(AccessNode):
@classmethod
def _literalParser(cls):
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, ExpressionNode.parse)
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE,
Parser.doAssert(ExpressionNode.parse, "expression"))

View File

@@ -21,7 +21,7 @@ class Program(Node):
ExpressionNode.parse,
ImportNode.parse,
StatementNode.parse,
exception = SyntaxException(f"Unknown statement: {input.current().pos}")
exception = SyntaxException(f"Invalid statement: {input.currentToEndOfLine()}", input.current().pos)
)(input)
root = Program()

View File

@@ -27,6 +27,6 @@ class ReturnNode(StatementNode):
return Parser.allOf(
Parser.terminalParser(TokenType.RETURN),
ExpressionNode.parse,
Parser.doAssert(ExpressionNode.parse, "expression"),
createNode=createNode
)(input)

View File

@@ -36,7 +36,7 @@ class TypedVariableNode(ExpressionNode):
return Parser.allOf(
Parser.terminalParser(TokenType.TYPE, lambda val, pos: TypeNode.withValue(val, pos)),
IdentifierNode.identifierParser(),
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
createNode=createNode
)

View File

@@ -1,5 +1,6 @@
from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.node.model import ParseResult, Node
from smnp.error.syntax import SyntaxException
def parse(input):
@@ -11,7 +12,7 @@ class Parser:
# a -> A
@staticmethod
def terminalParser(expectedType, createNode=None):
def terminalParser(expectedType, createNode=None, doAssert=False):
def provideNode(value, pos):
if createNode is None:
return IgnoredNode(pos)
@@ -22,6 +23,10 @@ class Parser:
token = input.current()
input.ahead()
return ParseResult.OK(provideNode(token.value, token.pos))
elif doAssert:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ""
raise SyntaxException(f"Expected '{expectedType.key}'{found}", input.currentPos())
return ParseResult.FAIL()
return parse
@@ -37,7 +42,11 @@ class Parser:
return value
if exception is not None:
raise exception
if callable(exception):
raise exception(input)
else:
raise exception
input.reset(snap)
return ParseResult.FAIL()
@@ -60,7 +69,10 @@ class Parser:
if not result.result:
if exception is not None:
raise exception
if callable(exception):
raise exception(input)
else:
raise exception
input.reset(snap)
return ParseResult.FAIL()
@@ -114,3 +126,16 @@ class Parser:
return parse
@staticmethod
def doAssert(parser, expected):
def parse(input):
result = parser(input)
if not result.result:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ''
raise SyntaxException(f"Expected {expected}{found}", input.currentPos())
return result
return parse

View File

@@ -5,4 +5,4 @@ def assertToken(expected, input):
if not input.hasCurrent():
raise SyntaxException(f"Expected '{expected}'")
if expected != input.current().type:
raise SyntaxException(f"Expected '{expected}', found '{input.current().value}'", input.current().pos)
raise SyntaxException(f"Expected '{expected}', found '{input.current().rawValue}'", input.current().pos)