From 86cf5d01f35720d3aeccafcca27208114ffbc41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Sat, 13 Jul 2019 14:48:58 +0200 Subject: [PATCH] Add 'throw' statement --- smnp/ast/node/statement.py | 4 +++- smnp/ast/node/throw.py | 17 +++++++++++++++++ smnp/error/custom.py | 9 +++++++++ smnp/runtime/evaluator.py | 5 ++++- smnp/runtime/evaluators/throw.py | 17 +++++++++++++++++ smnp/token/tokenizer.py | 1 + smnp/token/type.py | 1 + 7 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 smnp/ast/node/throw.py create mode 100644 smnp/error/custom.py create mode 100644 smnp/runtime/evaluators/throw.py diff --git a/smnp/ast/node/statement.py b/smnp/ast/node/statement.py index f3b78ba..dc0d922 100644 --- a/smnp/ast/node/statement.py +++ b/smnp/ast/node/statement.py @@ -10,12 +10,14 @@ def StatementParser(input): from smnp.ast.node.block import BlockParser from smnp.ast.node.condition import IfElseStatementParser from smnp.ast.node.expression import ExpressionParser - from smnp.ast.node.ret import ReturnParser + from smnp.ast.node.throw import ThrowParser + return Parser.oneOf( IfElseStatementParser, ExpressionParser, BlockParser, ReturnParser, + ThrowParser, name="statement" )(input) diff --git a/smnp/ast/node/throw.py b/smnp/ast/node/throw.py new file mode 100644 index 0000000..7e8c1cc --- /dev/null +++ b/smnp/ast/node/throw.py @@ -0,0 +1,17 @@ +from smnp.ast.node.expression import ExpressionParser +from smnp.ast.node.valuable import Valuable +from smnp.ast.parser import Parser +from smnp.token.type import TokenType + + +class Throw(Valuable): + pass + + +def ThrowParser(input): + return Parser.allOf( + Parser.terminal(TokenType.THROW), + Parser.doAssert(ExpressionParser, "error message as string"), + createNode=lambda throw, message: Throw.withValue(message, throw.pos), + name="throw" + )(input) \ No newline at end of file diff --git a/smnp/error/custom.py b/smnp/error/custom.py new file mode 100644 index 0000000..d9f6def --- /dev/null +++ b/smnp/error/custom.py @@ -0,0 +1,9 @@ +from smnp.error.base import SmnpException + + +class CustomException(SmnpException): + def __init__(self, message, pos): + super().__init__(message, pos) + + def _title(self): + return "Execution Error" diff --git a/smnp/runtime/evaluator.py b/smnp/runtime/evaluator.py index d722577..37f9fe4 100644 --- a/smnp/runtime/evaluator.py +++ b/smnp/runtime/evaluator.py @@ -5,6 +5,7 @@ from smnp.ast.node.function import FunctionDefinition from smnp.ast.node.imports import Import from smnp.ast.node.program import Program from smnp.ast.node.ret import Return +from smnp.ast.node.throw import Throw from smnp.error.runtime import RuntimeException from smnp.type.model import Type @@ -69,7 +70,6 @@ class EvaluationResult(): def evaluate(node, environment): from smnp.runtime.evaluators.program import ProgramEvaluator - from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.condition import IfElseStatementEvaluator from smnp.runtime.evaluators.block import BlockEvaluator @@ -77,6 +77,8 @@ def evaluate(node, environment): from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator from smnp.runtime.evaluators.function import ReturnEvaluator from smnp.runtime.evaluators.extend import ExtendEvaluator + from smnp.runtime.evaluators.throw import ThrowEvaluator + result = Evaluator.oneOf( Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse), @@ -85,6 +87,7 @@ def evaluate(node, environment): Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition), Evaluator.forNodes(ReturnEvaluator.evaluate, Return), Evaluator.forNodes(ExtendEvaluator.evaluate, Extend), + Evaluator.forNodes(ThrowEvaluator.evaluate, Throw), #Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), #Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), #Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), diff --git a/smnp/runtime/evaluators/throw.py b/smnp/runtime/evaluators/throw.py new file mode 100644 index 0000000..de06030 --- /dev/null +++ b/smnp/runtime/evaluators/throw.py @@ -0,0 +1,17 @@ +from smnp.error.custom import CustomException +from smnp.error.runtime import RuntimeException +from smnp.runtime.evaluator import Evaluator +from smnp.runtime.evaluators.expression import expressionEvaluator +from smnp.type.model import Type + + +class ThrowEvaluator(Evaluator): + + @classmethod + def evaluator(cls, node, environment): + string = expressionEvaluator(doAssert=True)(node.value, environment).value + + if string.type != Type.STRING: + raise RuntimeException(f"Only {Type.STRING.name.lower()} types can be thrown", node.value.pos) + + raise CustomException(string.value, node.pos) \ No newline at end of file diff --git a/smnp/token/tokenizer.py b/smnp/token/tokenizer.py index 9495dee..017b88a 100644 --- a/smnp/token/tokenizer.py +++ b/smnp/token/tokenizer.py @@ -50,6 +50,7 @@ tokenizers = ( separated(defaultTokenizer(TokenType.RETURN)), separated(defaultTokenizer(TokenType.EXTEND)), separated(defaultTokenizer(TokenType.IMPORT)), + separated(defaultTokenizer(TokenType.THROW)), separated(defaultTokenizer(TokenType.FROM)), separated(defaultTokenizer(TokenType.WITH)), separated(defaultTokenizer(TokenType.ELSE)), diff --git a/smnp/token/type.py b/smnp/token/type.py index 8c6aff2..7c47bc2 100644 --- a/smnp/token/type.py +++ b/smnp/token/type.py @@ -35,6 +35,7 @@ class TokenType(Enum): RETURN = 'return' EXTEND = 'extend' IMPORT = 'import' + THROW = 'throw' FROM = 'from' WITH = 'with' ELSE = 'else'