diff options
Diffstat (limited to 'ql/ast')
84 files changed, 1051 insertions, 0 deletions
diff --git a/ql/ast/checkers/__pycache__/dependency_checker.cpython-35.pyc b/ql/ast/checkers/__pycache__/dependency_checker.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..5be2eb2 --- /dev/null +++ b/ql/ast/checkers/__pycache__/dependency_checker.cpython-35.pyc diff --git a/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc b/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..bb81f4a --- /dev/null +++ b/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc diff --git a/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc b/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..3bdfae5 --- /dev/null +++ b/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc diff --git a/ql/ast/checkers/dependency_checker.py b/ql/ast/checkers/dependency_checker.py new file mode 100644 index 0000000..473ef6b --- /dev/null +++ b/ql/ast/checkers/dependency_checker.py @@ -0,0 +1,44 @@ +class DependencyChecker: + def __init__(self, combinations): + self.__errors = [] + self.__check_dependencies(combinations) + + @property + def errors(self): + return self.__errors + + def __check_dependencies(self, combinations): + for (identifier, identifier_children, identifier_metadata) in combinations: + children = self.__find_children(identifier, combinations) + + if identifier in children: + self.__errors.append('Cyclic dependency detected in question identifiers on line(s) {}' + .format(str(self.__find_lines(children, combinations))[1:-1])) + + def __find_children(self, identifier, combinations): + reference, reference_children, _ = combinations[0] + + if len(combinations) == 1 and identifier == reference: + return reference_children + elif len(combinations) == 1: + return [] + elif identifier == reference: + all_children = [] + + for new_identifier in reference_children: + all_children += self.__find_children(new_identifier, combinations[1:]) + + return all_children + reference_children + + return self.__find_children(identifier, combinations[1:]) + + @staticmethod + def __find_lines(children, combinations): + lines = [] + + for identifier, _, metadata in combinations: + for child in children: + if identifier == child: + lines.append(metadata.line) + + return lines diff --git a/ql/ast/checkers/question_checker.py b/ql/ast/checkers/question_checker.py new file mode 100644 index 0000000..5b255d8 --- /dev/null +++ b/ql/ast/checkers/question_checker.py @@ -0,0 +1,33 @@ +class QuestionChecker: + def __init__(self, questions): + self.__errors = [] + self.__warnings = [] + self.__check_questions(questions) + + @property + def errors(self): + return self.__errors + + @property + def warnings(self): + return self.__warnings + + def __check_questions(self, questions): + for question1 in questions: + for question2 in questions: + if question1 != question2: + equal_labels = question1['label'] == question2['label'] + equal_identifiers = question1['identifier'] == question2['identifier'] + different_types = question1['answer_type'] != question2['answer_type'] + + if all([equal_labels, equal_identifiers, different_types]): + self.__errors.append('Duplicate questions with different types found on lines {} and {}' + .format(question1['metadata'].line, question2['metadata'].line)) + + if equal_identifiers: + self.__errors.append('Duplicate question identifiers found on lines {} and {}' + .format(question1['metadata'].line, question2['metadata'].line)) + + if equal_labels: + self.__warnings.append('Duplicate question labels found on lines {} and {}' + .format(question1['metadata'].line, question2['metadata'].line)) diff --git a/ql/ast/checkers/reference_checker.py b/ql/ast/checkers/reference_checker.py new file mode 100644 index 0000000..71407c3 --- /dev/null +++ b/ql/ast/checkers/reference_checker.py @@ -0,0 +1,30 @@ +class ReferenceChecker: + def __init__(self, table): + self.__errors = [] + self.__check_references(table, [], []) + + @property + def errors(self): + return self.__errors + + def __check_references(self, table, known_types, unknown_types): + (local_known_types, local_unknown_types) = self.__split_known_unknown_types(table) + known_types += local_known_types + unknown_types += local_unknown_types + + self.__identifier_seen(known_types, unknown_types) + + if table['children']: + for child_scope in table['children']: + self.__check_references(child_scope, known_types, unknown_types) + + @staticmethod + def __split_known_unknown_types(table): + known_types = [row['name'] for row in table['content'] if row['type']] + unknown_types = [(row['name'], row['line']) for row in table['content'] if not (row['type'])] + return known_types, unknown_types + + def __identifier_seen(self, known_types, unknown_types): + for (identifier, line) in unknown_types: + if not (identifier in known_types): + self.__errors.append('Identifier \"{}\" at line {} is unknown'.format(identifier, line)) diff --git a/ql/ast/extractors/__pycache__/extractor.cpython-35.pyc b/ql/ast/extractors/__pycache__/extractor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..ceb2379 --- /dev/null +++ b/ql/ast/extractors/__pycache__/extractor.cpython-35.pyc diff --git a/ql/ast/extractors/extractor.py b/ql/ast/extractors/extractor.py new file mode 100644 index 0000000..f86304e --- /dev/null +++ b/ql/ast/extractors/extractor.py @@ -0,0 +1,35 @@ +from ql.ast.visitors.dependency_visitor import DependencyVisitor +from ql.ast.visitors.identifier_type_visitor import IdentifierTypeVisitor +from ql.ast.visitors.model_generator import ModelGenerator +from ql.ast.visitors.question_visitor import QuestionVisitor +from ql.ast.visitors.reference_visitor import ReferenceVisitor + + +def extract_identifier_dependencies(ast): + dependency_visitor = DependencyVisitor() + dependency_visitor.visit(ast) + return dependency_visitor.identifier_dependencies + + +def extract_identifier_scopes(ast): + reference_visitor = ReferenceVisitor() + reference_visitor.visit(ast) + return reference_visitor.identifier_scopes + + +def extract_identifier_types(ast): + identifier_type_visitor = IdentifierTypeVisitor() + identifier_type_visitor.visit(ast) + return identifier_type_visitor.symbol_table + + +def extract_questions(ast): + question_visitor = QuestionVisitor() + question_visitor.visit(ast) + return question_visitor.questions + + +def extract_gui_model(ast): + model_generator = ModelGenerator() + model_generator.visit(ast) + return model_generator.form diff --git a/ql/ast/nodes/__pycache__/base.cpython-35.pyc b/ql/ast/nodes/__pycache__/base.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..d1f8e50 --- /dev/null +++ b/ql/ast/nodes/__pycache__/base.cpython-35.pyc diff --git a/ql/ast/nodes/base.py b/ql/ast/nodes/base.py new file mode 100644 index 0000000..d9196f9 --- /dev/null +++ b/ql/ast/nodes/base.py @@ -0,0 +1,10 @@ +class BaseNode: + def __init__(self, metadata): + self.__metadata = metadata + + def accept(self, visitor): + visitor.visit(self) + + @property + def metadata(self): + return self.__metadata diff --git a/ql/ast/nodes/expressions/__pycache__/expression.cpython-35.pyc b/ql/ast/nodes/expressions/__pycache__/expression.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..84ede5c --- /dev/null +++ b/ql/ast/nodes/expressions/__pycache__/expression.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc b/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..b4ff8dc --- /dev/null +++ b/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/addition_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/addition_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..bbc8130 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/addition_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/and_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/and_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..f004296 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/and_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/binary_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/binary_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..18d3fc0 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/binary_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/division_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/division_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..9cd8e0a --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/division_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/equals_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/equals_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..5d48766 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/equals_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_equals_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_equals_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..888c85c --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_equals_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_than_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_than_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..be0e395 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_than_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/less_equals_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_equals_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..b7e8155 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_equals_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/less_than_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_than_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..850a39c --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_than_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/multiplication_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/multiplication_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..ff0e1ab --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/multiplication_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/not_equals_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/not_equals_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..fe666cf --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/not_equals_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/or_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/or_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..0b81fc3 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/or_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/__pycache__/subtraction_operator.cpython-35.pyc b/ql/ast/nodes/expressions/binary_operators/__pycache__/subtraction_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..cd89879 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/subtraction_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/binary_operators/addition_operator.py b/ql/ast/nodes/expressions/binary_operators/addition_operator.py new file mode 100644 index 0000000..d6e4506 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/addition_operator.py @@ -0,0 +1,27 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.decimal import QLDecimal +from ql.types.integer import QLInteger +from ql.types.money import QLMoney +from ql.types.undefined import QLUndefined + + +class AdditionOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(AdditionOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLDecimal, QLDecimal): QLDecimal, + (QLDecimal, QLInteger): QLDecimal, + (QLInteger, QLInteger): QLInteger, + (QLInteger, QLDecimal): QLDecimal, + (QLMoney, QLMoney): QLMoney, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value + self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/and_operator.py b/ql/ast/nodes/expressions/binary_operators/and_operator.py new file mode 100644 index 0000000..5981a6b --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/and_operator.py @@ -0,0 +1,21 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.boolean import QLBoolean +from ql.types.undefined import QLUndefined + + +class AndOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(AndOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLBoolean, QLBoolean): QLBoolean, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value and self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/binary_operator.py b/ql/ast/nodes/expressions/binary_operators/binary_operator.py new file mode 100644 index 0000000..048d927 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/binary_operator.py @@ -0,0 +1,17 @@ +from ql.ast.nodes.expressions.expression import ExpressionNode + + +class BinaryOperatorNode(ExpressionNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(BinaryOperatorNode, self).__init__(metadata, expression_type, value) + self.__left_expression = left_expression + self.__right_expression = right_expression + + @property + def left_expression(self): + return self.__left_expression + + @property + def right_expression(self): + return self.__right_expression + diff --git a/ql/ast/nodes/expressions/binary_operators/division_operator.py b/ql/ast/nodes/expressions/binary_operators/division_operator.py new file mode 100644 index 0000000..16d5063 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/division_operator.py @@ -0,0 +1,31 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.decimal import QLDecimal +from ql.types.integer import QLInteger +from ql.types.money import QLMoney +from ql.types.undefined import QLUndefined + + +class DivisionOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(DivisionOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLDecimal, QLDecimal): QLDecimal, + (QLDecimal, QLInteger): QLDecimal, + (QLInteger, QLInteger): QLDecimal, + (QLInteger, QLDecimal): QLDecimal, + (QLMoney, QLDecimal): QLMoney, + (QLMoney, QLInteger): QLMoney, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + try: + self.value = self.left_expression.value / self.right_expression.value + except ZeroDivisionError: + self.value = QLUndefined() diff --git a/ql/ast/nodes/expressions/binary_operators/equals_operator.py b/ql/ast/nodes/expressions/binary_operators/equals_operator.py new file mode 100644 index 0000000..21315f9 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/equals_operator.py @@ -0,0 +1,15 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.boolean import QLBoolean + + +class EqualsOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(EqualsOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + + @staticmethod + def get_result_type(): + return QLBoolean + + def evaluate(self): + self.value = self.left_expression.value == self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/greater_equals_operator.py b/ql/ast/nodes/expressions/binary_operators/greater_equals_operator.py new file mode 100644 index 0000000..0512688 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/greater_equals_operator.py @@ -0,0 +1,30 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +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.undefined import QLUndefined + + +class GreaterEqualsOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(GreaterEqualsOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = [ + (QLDate, QLDate), + (QLDecimal, QLDecimal), + (QLDecimal, QLInteger), + (QLInteger, QLInteger), + (QLInteger, QLDecimal), + (QLMoney, QLMoney), + ] + + def get_result_type(self): + if (self.left_expression.expression_type, self.right_expression.expression_type) in self.__valid_types: + return QLBoolean + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value >= self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/greater_than_operator.py b/ql/ast/nodes/expressions/binary_operators/greater_than_operator.py new file mode 100644 index 0000000..460c909 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/greater_than_operator.py @@ -0,0 +1,30 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +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.undefined import QLUndefined + + +class GreaterThanOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(GreaterThanOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = [ + (QLDate, QLDate), + (QLDecimal, QLDecimal), + (QLDecimal, QLInteger), + (QLInteger, QLInteger), + (QLInteger, QLDecimal), + (QLMoney, QLMoney), + ] + + def get_result_type(self): + if (self.left_expression.expression_type, self.right_expression.expression_type) in self.__valid_types: + return QLBoolean + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value > self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/less_equals_operator.py b/ql/ast/nodes/expressions/binary_operators/less_equals_operator.py new file mode 100644 index 0000000..f5b86df --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/less_equals_operator.py @@ -0,0 +1,30 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +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.undefined import QLUndefined + + +class LessEqualsOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(LessEqualsOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = [ + (QLDate, QLDate), + (QLDecimal, QLDecimal), + (QLDecimal, QLInteger), + (QLInteger, QLInteger), + (QLInteger, QLDecimal), + (QLMoney, QLMoney), + ] + + def get_result_type(self): + if (self.left_expression.expression_type, self.right_expression.expression_type) in self.__valid_types: + return QLBoolean + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value <= self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/less_than_operator.py b/ql/ast/nodes/expressions/binary_operators/less_than_operator.py new file mode 100644 index 0000000..65b456d --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/less_than_operator.py @@ -0,0 +1,30 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +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.undefined import QLUndefined + + +class LessThanOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(LessThanOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = [ + (QLDate, QLDate), + (QLDecimal, QLDecimal), + (QLDecimal, QLInteger), + (QLInteger, QLInteger), + (QLInteger, QLDecimal), + (QLMoney, QLMoney), + ] + + def get_result_type(self): + if (self.left_expression.expression_type, self.right_expression.expression_type) in self.__valid_types: + return QLBoolean + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value < self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/multiplication_operator.py b/ql/ast/nodes/expressions/binary_operators/multiplication_operator.py new file mode 100644 index 0000000..b014c0e --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/multiplication_operator.py @@ -0,0 +1,30 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.decimal import QLDecimal +from ql.types.integer import QLInteger +from ql.types.money import QLMoney +from ql.types.undefined import QLUndefined + + +class MultiplicationOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(MultiplicationOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLDecimal, QLDecimal): QLDecimal, + (QLDecimal, QLInteger): QLDecimal, + (QLDecimal, QLMoney): QLMoney, + (QLInteger, QLInteger): QLInteger, + (QLInteger, QLDecimal): QLDecimal, + (QLInteger, QLMoney): QLMoney, + (QLMoney, QLDecimal): QLMoney, + (QLMoney, QLInteger): QLMoney, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value * self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/not_equals_operator.py b/ql/ast/nodes/expressions/binary_operators/not_equals_operator.py new file mode 100644 index 0000000..3be2e5f --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/not_equals_operator.py @@ -0,0 +1,15 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.boolean import QLBoolean + + +class NotEqualsOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(NotEqualsOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + + @staticmethod + def get_result_type(): + return QLBoolean + + def evaluate(self): + self.value = self.left_expression.value != self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/or_operator.py b/ql/ast/nodes/expressions/binary_operators/or_operator.py new file mode 100644 index 0000000..4314868 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/or_operator.py @@ -0,0 +1,21 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.boolean import QLBoolean +from ql.types.undefined import QLUndefined + + +class OrOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(OrOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLBoolean, QLBoolean): QLBoolean, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value or self.right_expression.value diff --git a/ql/ast/nodes/expressions/binary_operators/subtraction_operator.py b/ql/ast/nodes/expressions/binary_operators/subtraction_operator.py new file mode 100644 index 0000000..8ebda15 --- /dev/null +++ b/ql/ast/nodes/expressions/binary_operators/subtraction_operator.py @@ -0,0 +1,27 @@ +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.types.decimal import QLDecimal +from ql.types.integer import QLInteger +from ql.types.money import QLMoney +from ql.types.undefined import QLUndefined + + +class SubtractionOperatorNode(BinaryOperatorNode): + def __init__(self, metadata, expression_type, left_expression, right_expression, value): + super(SubtractionOperatorNode, self).__init__(metadata, expression_type, + left_expression, right_expression, value) + self.__valid_types = { + (QLDecimal, QLDecimal): QLDecimal, + (QLDecimal, QLInteger): QLDecimal, + (QLInteger, QLInteger): QLInteger, + (QLInteger, QLDecimal): QLDecimal, + (QLMoney, QLMoney): QLMoney, + } + + def get_result_type(self): + if self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)): + return self.__valid_types.get((self.left_expression.expression_type, self.right_expression.expression_type)) + + return QLUndefined + + def evaluate(self): + self.value = self.left_expression.value - self.right_expression.value diff --git a/ql/ast/nodes/expressions/expression.py b/ql/ast/nodes/expressions/expression.py new file mode 100644 index 0000000..1364c0b --- /dev/null +++ b/ql/ast/nodes/expressions/expression.py @@ -0,0 +1,24 @@ +from ql.ast.nodes.base import BaseNode + + +class ExpressionNode(BaseNode): + def __init__(self, metadata, expression_type, value): + super(ExpressionNode, self).__init__(metadata) + self.__expression_type = expression_type + self.__value = value + + @property + def expression_type(self): + return self.__expression_type + + @expression_type.setter + def expression_type(self, value): + self.__expression_type = value + + @property + def value(self): + return self.__value + + @value.setter + def value(self, value): + self.__value = value diff --git a/ql/ast/nodes/expressions/literals/__pycache__/boolean_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/boolean_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..a6d43b1 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/boolean_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/date_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/date_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..33abf57 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/date_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/decimal_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/decimal_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..145ab16 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/decimal_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/integer_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/integer_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..33f5c62 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/integer_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..98af8e6 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/money_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/money_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..9dbf5d0 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/money_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/string_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/string_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..dbb3f72 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/string_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/__pycache__/undefined_literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/undefined_literal.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..73ce02b --- /dev/null +++ b/ql/ast/nodes/expressions/literals/__pycache__/undefined_literal.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/literals/boolean_literal.py b/ql/ast/nodes/expressions/literals/boolean_literal.py new file mode 100644 index 0000000..234e548 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/boolean_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class BooleanNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(BooleanNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/date_literal.py b/ql/ast/nodes/expressions/literals/date_literal.py new file mode 100644 index 0000000..971ab58 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/date_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class DateNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(DateNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/decimal_literal.py b/ql/ast/nodes/expressions/literals/decimal_literal.py new file mode 100644 index 0000000..7cc4eb5 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/decimal_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class DecimalNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(DecimalNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/integer_literal.py b/ql/ast/nodes/expressions/literals/integer_literal.py new file mode 100644 index 0000000..36923f9 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/integer_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class IntegerNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(IntegerNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/literal.py b/ql/ast/nodes/expressions/literals/literal.py new file mode 100644 index 0000000..a0e03ae --- /dev/null +++ b/ql/ast/nodes/expressions/literals/literal.py @@ -0,0 +1,14 @@ +from ql.ast.nodes.expressions.expression import ExpressionNode + + +class LiteralNode(ExpressionNode): + def __init__(self, metadata, expression_type, value): + super(LiteralNode, self).__init__(metadata, expression_type, value) + + @ExpressionNode.expression_type.setter + def expression_type(self, unused): + pass + + @ExpressionNode.value.setter + def value(self, unused): + pass diff --git a/ql/ast/nodes/expressions/literals/money_literal.py b/ql/ast/nodes/expressions/literals/money_literal.py new file mode 100644 index 0000000..119e5ff --- /dev/null +++ b/ql/ast/nodes/expressions/literals/money_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class MoneyNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(MoneyNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/string_literal.py b/ql/ast/nodes/expressions/literals/string_literal.py new file mode 100644 index 0000000..4fac895 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/string_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class StringNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(StringNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/literals/undefined_literal.py b/ql/ast/nodes/expressions/literals/undefined_literal.py new file mode 100644 index 0000000..4109df6 --- /dev/null +++ b/ql/ast/nodes/expressions/literals/undefined_literal.py @@ -0,0 +1,6 @@ +from ql.ast.nodes.expressions.literals.literal import LiteralNode + + +class UndefinedNode(LiteralNode): + def __init__(self, metadata, expression_type, value): + super(UndefinedNode, self).__init__(metadata, expression_type, value) diff --git a/ql/ast/nodes/expressions/unary_operators/__pycache__/negation_operator.cpython-35.pyc b/ql/ast/nodes/expressions/unary_operators/__pycache__/negation_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..9a6e409 --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/negation_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/unary_operators/__pycache__/negative_operator.cpython-35.pyc b/ql/ast/nodes/expressions/unary_operators/__pycache__/negative_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..45ad495 --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/negative_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/unary_operators/__pycache__/unary_operator.cpython-35.pyc b/ql/ast/nodes/expressions/unary_operators/__pycache__/unary_operator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..f72b4f0 --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/unary_operator.cpython-35.pyc diff --git a/ql/ast/nodes/expressions/unary_operators/negation_operator.py b/ql/ast/nodes/expressions/unary_operators/negation_operator.py new file mode 100644 index 0000000..7b6b0c5 --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/negation_operator.py @@ -0,0 +1,20 @@ +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +from ql.types.boolean import QLBoolean +from ql.types.undefined import QLUndefined + + +class NegationOperatorNode(UnaryOperatorNode): + def __init__(self, metadata, expression_type, expression, value): + super(NegationOperatorNode, self).__init__(metadata, expression_type, expression, value) + self.__valid_types = { + QLBoolean: QLBoolean, + } + + def get_result_type(self): + if self.__valid_types.get(self.expression.expression_type): + return self.__valid_types.get(self.expression.expression_type) + + return QLUndefined + + def evaluate(self): + self.value = QLBoolean(not self.expression.value) diff --git a/ql/ast/nodes/expressions/unary_operators/negative_operator.py b/ql/ast/nodes/expressions/unary_operators/negative_operator.py new file mode 100644 index 0000000..359ed2e --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/negative_operator.py @@ -0,0 +1,23 @@ +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +from ql.types.decimal import QLDecimal +from ql.types.integer import QLInteger +from ql.types.money import QLMoney +from ql.types.undefined import QLUndefined + + +class NegativeOperatorNode(UnaryOperatorNode): + def __init__(self, metadata, expression_type, expression, value): + super(NegativeOperatorNode, self).__init__(metadata, expression_type, expression, value) + self.__valid_types = { + QLDecimal: QLDecimal, + QLInteger: QLInteger, + QLMoney: QLMoney} + + def get_result_type(self): + if self.__valid_types.get(self.expression.expression_type): + return self.__valid_types.get(self.expression.expression_type) + + return QLUndefined + + def evaluate(self): + self.value = -self.expression.value diff --git a/ql/ast/nodes/expressions/unary_operators/unary_operator.py b/ql/ast/nodes/expressions/unary_operators/unary_operator.py new file mode 100644 index 0000000..fa75249 --- /dev/null +++ b/ql/ast/nodes/expressions/unary_operators/unary_operator.py @@ -0,0 +1,11 @@ +from ql.ast.nodes.expressions.expression import ExpressionNode + + +class UnaryOperatorNode(ExpressionNode): + def __init__(self, metadata, expression_type, expression, value): + super(UnaryOperatorNode, self).__init__(metadata, expression_type, value) + self.__expression = expression + + @property + def expression(self): + return self.__expression diff --git a/ql/ast/nodes/expressions/variable.py b/ql/ast/nodes/expressions/variable.py new file mode 100644 index 0000000..c126372 --- /dev/null +++ b/ql/ast/nodes/expressions/variable.py @@ -0,0 +1,11 @@ +from ql.ast.nodes.expressions.expression import ExpressionNode + + +class VariableNode(ExpressionNode): + def __init__(self, metadata, expression_type, identifier, value): + super(VariableNode, self).__init__(metadata, expression_type, value) + self.__identifier = identifier + + @property + def identifier(self): + return self.__identifier diff --git a/ql/ast/nodes/statements/__pycache__/block_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/block_statement.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..8ec170d --- /dev/null +++ b/ql/ast/nodes/statements/__pycache__/block_statement.cpython-35.pyc diff --git a/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..e10775a --- /dev/null +++ b/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc diff --git a/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..e81cfc1 --- /dev/null +++ b/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc diff --git a/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..6b86ed9 --- /dev/null +++ b/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc diff --git a/ql/ast/nodes/statements/block_statement.py b/ql/ast/nodes/statements/block_statement.py new file mode 100644 index 0000000..2013c82 --- /dev/null +++ b/ql/ast/nodes/statements/block_statement.py @@ -0,0 +1,11 @@ +from ql.ast.nodes.base import BaseNode + + +class BlockStatementNode(BaseNode): + def __init__(self, metadata, block): + super(BlockStatementNode, self).__init__(metadata) + self.__block = block + + @property + def block(self): + return self.__block diff --git a/ql/ast/nodes/statements/form_statement.py b/ql/ast/nodes/statements/form_statement.py new file mode 100644 index 0000000..0030623 --- /dev/null +++ b/ql/ast/nodes/statements/form_statement.py @@ -0,0 +1,12 @@ +from ql.ast.nodes.statements.block_statement import BlockStatementNode + + +class FormNode(BlockStatementNode): + def __init__(self, metadata, block, identifier): + super(FormNode, self).__init__(metadata, block) + self.__identifier = identifier + + @property + def identifier(self): + return self.__identifier + diff --git a/ql/ast/nodes/statements/if_statement.py b/ql/ast/nodes/statements/if_statement.py new file mode 100644 index 0000000..68845b8 --- /dev/null +++ b/ql/ast/nodes/statements/if_statement.py @@ -0,0 +1,11 @@ +from ql.ast.nodes.statements.block_statement import BlockStatementNode + + +class IfNode(BlockStatementNode): + def __init__(self, metadata, block, condition): + super(IfNode, self).__init__(metadata, block) + self.__condition = condition + + @property + def condition(self): + return self.__condition diff --git a/ql/ast/nodes/statements/question_statement.py b/ql/ast/nodes/statements/question_statement.py new file mode 100644 index 0000000..2878449 --- /dev/null +++ b/ql/ast/nodes/statements/question_statement.py @@ -0,0 +1,31 @@ +from ql.ast.nodes.base import BaseNode + + +class QuestionNode(BaseNode): + def __init__(self, line_number, label, identifier, answer_type, answer, computed): + super(QuestionNode, self).__init__(line_number) + self.__label = label + self.__identifier = identifier + self.__answer_type = answer_type + self.__answer = answer + self.__computed = computed + + @property + def label(self): + return self.__label + + @property + def identifier(self): + return self.__identifier + + @property + def answer_type(self): + return self.__answer_type + + @property + def answer(self): + return self.__answer + + @property + def computed(self): + return self.__computed diff --git a/ql/ast/visitors/__pycache__/dependency_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/dependency_visitor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..8d348e6 --- /dev/null +++ b/ql/ast/visitors/__pycache__/dependency_visitor.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc b/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..29a2e9b --- /dev/null +++ b/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..89f690d --- /dev/null +++ b/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc b/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..9950522 --- /dev/null +++ b/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..ac60a13 --- /dev/null +++ b/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..e0491b9 --- /dev/null +++ b/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc diff --git a/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..a2df3f2 --- /dev/null +++ b/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc diff --git a/ql/ast/visitors/dependency_visitor.py b/ql/ast/visitors/dependency_visitor.py new file mode 100644 index 0000000..8bb81eb --- /dev/null +++ b/ql/ast/visitors/dependency_visitor.py @@ -0,0 +1,54 @@ +from multimethods import multimethod + +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.ast.nodes.expressions.literals.literal import LiteralNode +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +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 + + +class DependencyVisitor: + def __init__(self): + self.__all_dependencies = [] + self.__current_dependencies = [] + + @property + def identifier_dependencies(self): + return self.__all_dependencies + + @multimethod(FormNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(IfNode) + def visit(self, node): + node.condition.accept(self) + for child in node.block: + child.accept(self) + + @multimethod(QuestionNode) + def visit(self, node): + if node.computed: + self.__current_dependencies = [] + node.answer.accept(self) + self.__all_dependencies.append((node.identifier, self.__current_dependencies, node.metadata)) + + @multimethod(BinaryOperatorNode) + def visit(self, node): + node.left_expression.accept(self) + node.right_expression.accept(self) + + @multimethod(UnaryOperatorNode) + def visit(self, node): + node.expression.accept(self) + + @multimethod(VariableNode) + def visit(self, node): + self.__current_dependencies.append(node.identifier) + + @multimethod(LiteralNode) + def visit(self, node): + pass diff --git a/ql/ast/visitors/expression_evaluator.py b/ql/ast/visitors/expression_evaluator.py new file mode 100644 index 0000000..9306d98 --- /dev/null +++ b/ql/ast/visitors/expression_evaluator.py @@ -0,0 +1,37 @@ +from multimethods import multimethod + +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.ast.nodes.expressions.literals.literal import LiteralNode +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +from ql.ast.nodes.expressions.variable import VariableNode +from ql.types.undefined import QLUndefined + + +class ExpressionEvaluator: + def __init__(self, form): + self.form = form + self.result = QLUndefined() + + @multimethod(BinaryOperatorNode) + def visit(self, node): + node.left_expression.accept(self) + node.right_expression.accept(self) + node.evaluate() + self.result = node.value + + @multimethod(UnaryOperatorNode) + def visit(self, node): + node.expression.accept(self) + node.evaluate() + self.result = node.value + + @multimethod(LiteralNode) + def visit(self, node): + self.result = node.value + + @multimethod(VariableNode) + def visit(self, node): + for question in self.form.block: + if node.identifier == question.identifier: + node.value = question.answer.value + self.result = node.value diff --git a/ql/ast/visitors/identifier_type_visitor.py b/ql/ast/visitors/identifier_type_visitor.py new file mode 100644 index 0000000..d064f5b --- /dev/null +++ b/ql/ast/visitors/identifier_type_visitor.py @@ -0,0 +1,31 @@ +from multimethods import multimethod + +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 + + +class IdentifierTypeVisitor: + def __init__(self): + self.__symbol_table = [] + + @property + def symbol_table(self): + return self.__symbol_table + + @multimethod(FormNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(IfNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(QuestionNode) + def visit(self, node): + self.__symbol_table.append({ + 'identifier': node.identifier, + 'answer_type': node.answer_type, + }) diff --git a/ql/ast/visitors/model_generator.py b/ql/ast/visitors/model_generator.py new file mode 100644 index 0000000..f2cf5d8 --- /dev/null +++ b/ql/ast/visitors/model_generator.py @@ -0,0 +1,39 @@ +from multimethods import multimethod + +from gui.model.form import FormModel +from gui.model.question import QuestionModel +from ql.ast.nodes.expressions.binary_operators.and_operator import AndOperatorNode +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.types.boolean import QLBoolean +from ql.types.undefined import QLUndefined + + +class ModelGenerator: + def __init__(self): + self.form = None + self.condition = QLBoolean.get_literal_node(True) + + @multimethod(FormNode) + def visit(self, node): + self.form = FormModel(node.identifier) + + for child in node.block: + child.accept(self) + + @multimethod(IfNode) + def visit(self, node): + previous_condition = self.condition + self.condition = AndOperatorNode(None, QLBoolean, self.condition, node.condition, QLUndefined()) + + for child in node.block: + child.accept(self) + + self.condition = previous_condition + + @multimethod(QuestionNode) + def visit(self, node): + question_model = QuestionModel(node.label, node.identifier, node.answer_type, + node.answer, node.computed, self.condition) + self.form.block.append(question_model) diff --git a/ql/ast/visitors/question_visitor.py b/ql/ast/visitors/question_visitor.py new file mode 100644 index 0000000..8037364 --- /dev/null +++ b/ql/ast/visitors/question_visitor.py @@ -0,0 +1,33 @@ +from multimethods import multimethod + +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 + + +class QuestionVisitor: + def __init__(self): + self.__questions = [] + + @property + def questions(self): + return self.__questions + + @multimethod(FormNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(IfNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(QuestionNode) + def visit(self, node): + self.__questions.append({ + 'label': node.label, + 'identifier': node.identifier, + 'answer_type': node.answer_type, + 'metadata': node.metadata, + }) diff --git a/ql/ast/visitors/reference_visitor.py b/ql/ast/visitors/reference_visitor.py new file mode 100644 index 0000000..5cbbcc7 --- /dev/null +++ b/ql/ast/visitors/reference_visitor.py @@ -0,0 +1,86 @@ +from multimethods import multimethod + +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.ast.nodes.expressions.literals.literal import LiteralNode +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +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 + + +class ReferenceVisitor: + def __init__(self): + self.__current_block = [] + self.__current_scope = {} + + @property + def identifier_scopes(self): + return self.__current_scope + + @multimethod(FormNode) + def visit(self, node): + self.__current_scope = { + 'content': [], + 'children': [], + } + self.__current_block = [] + + for child in node.block: + child.accept(self) + + self.__current_scope['content'] = self.__current_block + + @multimethod(IfNode) + def visit(self, node): + node.condition.accept(self) + + self.__current_scope['content'] += self.__current_block + previous_scope = self.__current_scope + previous_block = self.__current_block + self.__current_block = [] + self.__current_scope = { + 'content': [], + 'children': [], + } + + for child in node.block: + child.accept(self) + + self.__current_scope['content'] = self.__current_block + previous_scope['children'].append(self.__current_scope) + self.__current_block = previous_block + self.__current_scope = previous_scope + + @multimethod(QuestionNode) + def visit(self, node): + self.__current_block.append(dict({ + 'name': node.identifier, + 'type': node.answer_type, + 'line': node.metadata.line, + })) + + if node.computed: + node.answer.accept(self) + + @multimethod(BinaryOperatorNode) + def visit(self, node): + node.left_expression.accept(self) + node.right_expression.accept(self) + + @multimethod(UnaryOperatorNode) + def visit(self, node): + node.expression.accept(self) + + @multimethod(VariableNode) + def visit(self, node): + name = node.identifier + self.__current_block.append({ + 'name': name, + 'type': [], + 'line': node.metadata.line, + }) + + @multimethod(LiteralNode) + def visit(self, node): + pass diff --git a/ql/ast/visitors/type_visitor.py b/ql/ast/visitors/type_visitor.py new file mode 100644 index 0000000..7f4ac7c --- /dev/null +++ b/ql/ast/visitors/type_visitor.py @@ -0,0 +1,85 @@ +from multimethods import multimethod + +from ql.ast.nodes.expressions.binary_operators.binary_operator import BinaryOperatorNode +from ql.ast.nodes.expressions.literals.literal import LiteralNode +from ql.ast.nodes.expressions.unary_operators.unary_operator import UnaryOperatorNode +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.types.boolean import QLBoolean +from ql.types.undefined import QLUndefined + + +class TypeVisitor: + def __init__(self, symbol_table): + self.__symbol_table = symbol_table + self.__errors = [] + + @property + def errors(self): + return self.__errors + + @multimethod(FormNode) + def visit(self, node): + for child in node.block: + child.accept(self) + + @multimethod(IfNode) + def visit(self, node): + node.condition.accept(self) + + if node.condition.expression_type != QLBoolean: + self.__errors.append('Condition does not evaluate to boolean on line {}.' + .format(node.metadata.line)) + + for child in node.block: + child.accept(self) + + @multimethod(QuestionNode) + def visit(self, node): + if node.computed: + node.answer.accept(self) + + result_type = node.answer.expression_type + + if node.answer_type != result_type: + self.__errors.append('Expression not of type {} on line {}' + .format(node.answer_type, node.answer.metadata.line)) + + @multimethod(BinaryOperatorNode) + def visit(self, node): + node.left_expression.accept(self) + node.right_expression.accept(self) + + result_type = node.get_result_type() + + if result_type == QLUndefined: + self.__errors.append('Invalid operands: {} and {} not supported for binary operation on line {}' + .format(node.left_expression.expression_type, + node.right_expression.expression_type, + node.metadata.line)) + + node.expression_type = result_type + + @multimethod(UnaryOperatorNode) + def visit(self, node): + node.expression.accept(self) + + result_type = node.get_result_type() + + if result_type == QLUndefined: + self.__errors.append('Invalid operand: {} not supported for unary operation on line {}' + .format(node.expression.expression_type, node.metadata.line)) + + node.expression_type = result_type + + @multimethod(VariableNode) + def visit(self, node): + for row in self.__symbol_table: + if row['identifier'] == node.identifier: + node.expression_type = row['answer_type'] + + @multimethod(LiteralNode) + def visit(self, node): + pass |