aboutsummaryrefslogtreecommitdiff
path: root/ql/parser/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'ql/parser/parser.py')
-rw-r--r--ql/parser/parser.py319
1 files changed, 319 insertions, 0 deletions
diff --git a/ql/parser/parser.py b/ql/parser/parser.py
new file mode 100644
index 0000000..d962a0f
--- /dev/null
+++ b/ql/parser/parser.py
@@ -0,0 +1,319 @@
+from ply.yacc import yacc
+
+from ql.ast.nodes.expressions.binary_operators.addition_operator import AdditionOperatorNode
+from ql.ast.nodes.expressions.binary_operators.and_operator import AndOperatorNode
+from ql.ast.nodes.expressions.binary_operators.division_operator import DivisionOperatorNode
+from ql.ast.nodes.expressions.binary_operators.equals_operator import EqualsOperatorNode
+from ql.ast.nodes.expressions.binary_operators.greater_equals_operator import GreaterEqualsOperatorNode
+from ql.ast.nodes.expressions.binary_operators.greater_than_operator import GreaterThanOperatorNode
+from ql.ast.nodes.expressions.binary_operators.less_equals_operator import LessEqualsOperatorNode
+from ql.ast.nodes.expressions.binary_operators.less_than_operator import LessThanOperatorNode
+from ql.ast.nodes.expressions.binary_operators.multiplication_operator import MultiplicationOperatorNode
+from ql.ast.nodes.expressions.binary_operators.not_equals_operator import NotEqualsOperatorNode
+from ql.ast.nodes.expressions.binary_operators.or_operator import OrOperatorNode
+from ql.ast.nodes.expressions.binary_operators.subtraction_operator import SubtractionOperatorNode
+from ql.ast.nodes.expressions.literals.boolean_literal import BooleanNode
+from ql.ast.nodes.expressions.literals.date_literal import DateNode
+from ql.ast.nodes.expressions.literals.decimal_literal import DecimalNode
+from ql.ast.nodes.expressions.literals.integer_literal import IntegerNode
+from ql.ast.nodes.expressions.literals.money_literal import MoneyNode
+from ql.ast.nodes.expressions.literals.string_literal import StringNode
+from ql.ast.nodes.expressions.unary_operators.negation_operator import NegationOperatorNode
+from ql.ast.nodes.expressions.unary_operators.negative_operator import NegativeOperatorNode
+from ql.ast.nodes.expressions.variable import VariableNode
+from ql.ast.nodes.statements.form_statement import FormNode
+from ql.ast.nodes.statements.if_statement import IfNode
+from ql.ast.nodes.statements.question_statement import QuestionNode
+from ql.parser.lexer import QLLexer
+from ql.parser.metadata import Metadata
+from ql.types.boolean import QLBoolean
+from ql.types.date import QLDate
+from ql.types.decimal import QLDecimal
+from ql.types.integer import QLInteger
+from ql.types.money import QLMoney
+from ql.types.string import QLString
+from ql.types.undefined import QLUndefined
+
+
+class QLParser:
+ def __init__(self):
+ self.__errors = []
+ self.__tokens = QLLexer.tokens
+ self.__parser = None
+ self.__precedence = (
+ ('left', 'OR'),
+ ('left', 'AND'),
+ ('nonassoc', 'EQUALS', 'NOT_EQUALS'),
+ ('nonassoc', 'LESS_EQUALS', 'LESS_THAN', 'GREATER_EQUALS', 'GREATER_THAN'),
+ ('left', 'PLUS', 'MINUS'),
+ ('left', 'TIMES', 'DIVIDE'),
+ ('right', 'NOT'),
+ )
+
+ @property
+ def errors(self):
+ return self.__errors
+
+ @property
+ def tokens(self):
+ return self.__tokens
+
+ @property
+ def parser(self):
+ return self.__parser
+
+ @property
+ def precedence(self):
+ return self.__precedence
+
+ def build(self):
+ self.__parser = yacc(module=self)
+
+ def parse(self, data, lexer):
+ self.__errors = []
+ return self.parser.parse(data, lexer)
+
+ # Grammar
+ @staticmethod
+ def p_form(production):
+ """form : FORM IDENTIFIER block"""
+ production[0] = FormNode(Metadata(production.lineno(1)), production[3], production[2])
+
+ @staticmethod
+ def p_block(production):
+ """block : LEFT_BRACE statements RIGHT_BRACE"""
+ production[0] = production[2]
+
+ # Statements
+ @staticmethod
+ def p_statements(production):
+ """statements : statement statements
+ | statement"""
+ if len(production) == 3:
+ production[0] = [production[1]] + production[2]
+ elif len(production) == 2:
+ production[0] = [production[1]]
+
+ @staticmethod
+ def p_statement(production):
+ """statement : if
+ | question"""
+ production[0] = production[1]
+
+ # Questions
+ @staticmethod
+ def p_question(production):
+ """question : STRING_LITERAL IDENTIFIER COLON type"""
+ production[0] = QuestionNode(Metadata(production.lineno(1)), production[1], production[2],
+ production[4], production[4].get_literal_node(production[4]()), False)
+
+ @staticmethod
+ def p_question_computed(production):
+ """question : STRING_LITERAL IDENTIFIER COLON type ASSIGN expression"""
+ production[0] = QuestionNode(Metadata(production.lineno(1)), production[1], production[2],
+ production[4], production[6], True)
+
+ # Control flow
+ @staticmethod
+ def p_if(production):
+ """if : IF condition block"""
+ production[0] = IfNode(Metadata(production.lineno(1)), production[3], production[2])
+
+ @staticmethod
+ def p_condition(production):
+ """condition : LEFT_BRACKET expression RIGHT_BRACKET"""
+ production[0] = production[2]
+
+ # Expressions
+ @staticmethod
+ def p_parenthesis(production):
+ """expression : LEFT_BRACKET expression RIGHT_BRACKET"""
+ production[0] = production[2]
+
+ @staticmethod
+ def p_variable(production):
+ """expression : IDENTIFIER"""
+ production[0] = VariableNode(Metadata(production.lineno(1)), QLUndefined, production[1],
+ QLUndefined())
+
+ # Unary operators
+ @staticmethod
+ def p_not(production):
+ """expression : NOT expression"""
+ production[0] = NegationOperatorNode(Metadata(production.lineno(1)), QLBoolean,
+ production[2], QLUndefined())
+
+ @staticmethod
+ def p_negative(production):
+ """expression : MINUS expression"""
+ production[0] = NegativeOperatorNode(Metadata(production.lineno(1)), QLUndefined,
+ production[2], QLUndefined())
+
+ # Binary operators
+ @staticmethod
+ def p_and(production):
+ """expression : expression AND expression"""
+ production[0] = AndOperatorNode(Metadata(production.lineno(2)), QLBoolean, production[1],
+ production[3], QLUndefined())
+
+ @staticmethod
+ def p_or(production):
+ """expression : expression OR expression"""
+ production[0] = OrOperatorNode(Metadata(production.lineno(2)), QLBoolean, production[1],
+ production[3], QLUndefined())
+
+ @staticmethod
+ def p_plus(production):
+ """expression : expression PLUS expression"""
+ production[0] = AdditionOperatorNode(Metadata(production.lineno(2)), QLUndefined,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_minus(production):
+ """expression : expression MINUS expression"""
+ production[0] = SubtractionOperatorNode(Metadata(production.lineno(2)), QLUndefined,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_times(production):
+ """expression : expression TIMES expression"""
+ production[0] = MultiplicationOperatorNode(Metadata(production.lineno(2)), QLUndefined,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_divide(production):
+ """expression : expression DIVIDE expression"""
+ production[0] = DivisionOperatorNode(Metadata(production.lineno(2)), QLUndefined,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_equals(production):
+ """expression : expression EQUALS expression"""
+ production[0] = EqualsOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_not_equals(production):
+ """expression : expression NOT_EQUALS expression"""
+ production[0] = NotEqualsOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_less_equals(production):
+ """expression : expression LESS_EQUALS expression"""
+ production[0] = LessEqualsOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_less_than(production):
+ """expression : expression LESS_THAN expression"""
+ production[0] = LessThanOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_greater_equals(production):
+ """expression : expression GREATER_EQUALS expression"""
+ production[0] = GreaterEqualsOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ @staticmethod
+ def p_greater_than(production):
+ """expression : expression GREATER_THAN expression"""
+ production[0] = GreaterThanOperatorNode(Metadata(production.lineno(2)), QLBoolean,
+ production[1], production[3], QLUndefined())
+
+ # Literals
+ @staticmethod
+ def p_boolean_literal(production):
+ """expression : FALSE
+ | TRUE"""
+ production[0] = BooleanNode(Metadata(production.lineno(1)), QLBoolean, QLBoolean(production[1]))
+
+ @staticmethod
+ def p_date_literal(production):
+ """expression : DATE_LITERAL"""
+ day, month, year = production[1]
+ production[0] = DateNode(Metadata(production.lineno(1)), QLDate, QLDate(day, month, year))
+
+ @staticmethod
+ def p_integer_literal(production):
+ """expression : INTEGER_LITERAL"""
+ production[0] = IntegerNode(Metadata(production.lineno(1)), QLInteger, QLInteger(production[1]))
+
+ @staticmethod
+ def p_decimal_literal(production):
+ """expression : DECIMAL_LITERAL"""
+ production[0] = DecimalNode(Metadata(production.lineno(1)), QLDecimal, QLDecimal(production[1]))
+
+ @staticmethod
+ def p_money_literal(production):
+ """expression : currency DECIMAL_LITERAL
+ | currency INTEGER_LITERAL"""
+ production[0] = MoneyNode(Metadata(production.lineno(1)), QLMoney,
+ QLMoney(production[2], production[1]))
+
+ @staticmethod
+ def p_string_literal(production):
+ """expression : STRING_LITERAL"""
+ production[0] = StringNode(Metadata(production.lineno(1)), QLString, QLString([1]))
+
+ # Currencies
+ @staticmethod
+ def p_currency(production):
+ """currency : DOLLAR
+ | RUBLE"""
+ production[0] = production[1]
+
+ # Types
+ @staticmethod
+ def p_boolean(production):
+ """type : BOOLEAN"""
+ production[0] = QLBoolean
+
+ @staticmethod
+ def p_decimal(production):
+ """type : DECIMAL"""
+ production[0] = QLDecimal
+
+ @staticmethod
+ def p_string(production):
+ """type : STRING"""
+ production[0] = QLString
+
+ @staticmethod
+ def p_date(production):
+ """type : DATE"""
+ production[0] = QLDate
+
+ @staticmethod
+ def p_money(production):
+ """type : MONEY"""
+ production[0] = QLMoney
+
+ @staticmethod
+ def p_integer(production):
+ """type : INTEGER"""
+ production[0] = QLInteger
+
+ # Error
+ def p_error(self, production):
+ if production is None:
+ self.errors.append('Syntax error.')
+ else:
+ self.errors.append('Syntax error at line {}, token={}.'.format(production.lineno, production.type))
+
+ def p_form_label(self, production):
+ """form : FORM LEFT_BRACE"""
+ self.errors.append('Missing form identifier at line {}.'.format(production.lineno(1)))
+
+ def p_empty_form(self, production):
+ """form : FORM IDENTIFIER LEFT_BRACE RIGHT_BRACE"""
+ self.errors.append('Empty form at line {}.'.format(production.lineno(1)))
+
+ def p_empty_if(self, production):
+ """statement : IF condition LEFT_BRACE RIGHT_BRACE"""
+ self.errors.append('Empty if block at line {}.'.format(production.lineno(1)))
+
+ def p_empty_condition(self, production):
+ """condition : LEFT_BRACKET RIGHT_BRACKET"""
+ self.errors.append('Empty conditional at line {}.'.format(production.lineno(1)))