aboutsummaryrefslogtreecommitdiff
path: root/ql/ast
diff options
context:
space:
mode:
Diffstat (limited to 'ql/ast')
-rw-r--r--ql/ast/checkers/__pycache__/dependency_checker.cpython-35.pycbin0 -> 1953 bytes
-rw-r--r--ql/ast/checkers/__pycache__/question_checker.cpython-35.pycbin0 -> 1658 bytes
-rw-r--r--ql/ast/checkers/__pycache__/reference_checker.cpython-35.pycbin0 -> 2061 bytes
-rw-r--r--ql/ast/checkers/dependency_checker.py44
-rw-r--r--ql/ast/checkers/question_checker.py33
-rw-r--r--ql/ast/checkers/reference_checker.py30
-rw-r--r--ql/ast/extractors/__pycache__/extractor.cpython-35.pycbin0 -> 1540 bytes
-rw-r--r--ql/ast/extractors/extractor.py35
-rw-r--r--ql/ast/nodes/__pycache__/base.cpython-35.pycbin0 -> 764 bytes
-rw-r--r--ql/ast/nodes/base.py10
-rw-r--r--ql/ast/nodes/expressions/__pycache__/expression.cpython-35.pycbin0 -> 1222 bytes
-rw-r--r--ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pycbin0 -> 843 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/addition_operator.cpython-35.pycbin0 -> 1523 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/and_operator.cpython-35.pycbin0 -> 1325 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/binary_operator.cpython-35.pycbin0 -> 1117 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/division_operator.cpython-35.pycbin0 -> 1604 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/equals_operator.cpython-35.pycbin0 -> 1142 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/greater_equals_operator.cpython-35.pycbin0 -> 1626 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/greater_than_operator.cpython-35.pycbin0 -> 1614 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/less_equals_operator.cpython-35.pycbin0 -> 1608 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/less_than_operator.cpython-35.pycbin0 -> 1596 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/multiplication_operator.cpython-35.pycbin0 -> 1601 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/not_equals_operator.cpython-35.pycbin0 -> 1158 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/or_operator.cpython-35.pycbin0 -> 1319 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/__pycache__/subtraction_operator.cpython-35.pycbin0 -> 1541 bytes
-rw-r--r--ql/ast/nodes/expressions/binary_operators/addition_operator.py27
-rw-r--r--ql/ast/nodes/expressions/binary_operators/and_operator.py21
-rw-r--r--ql/ast/nodes/expressions/binary_operators/binary_operator.py17
-rw-r--r--ql/ast/nodes/expressions/binary_operators/division_operator.py31
-rw-r--r--ql/ast/nodes/expressions/binary_operators/equals_operator.py15
-rw-r--r--ql/ast/nodes/expressions/binary_operators/greater_equals_operator.py30
-rw-r--r--ql/ast/nodes/expressions/binary_operators/greater_than_operator.py30
-rw-r--r--ql/ast/nodes/expressions/binary_operators/less_equals_operator.py30
-rw-r--r--ql/ast/nodes/expressions/binary_operators/less_than_operator.py30
-rw-r--r--ql/ast/nodes/expressions/binary_operators/multiplication_operator.py30
-rw-r--r--ql/ast/nodes/expressions/binary_operators/not_equals_operator.py15
-rw-r--r--ql/ast/nodes/expressions/binary_operators/or_operator.py21
-rw-r--r--ql/ast/nodes/expressions/binary_operators/subtraction_operator.py27
-rw-r--r--ql/ast/nodes/expressions/expression.py24
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/boolean_literal.cpython-35.pycbin0 -> 669 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/date_literal.cpython-35.pycbin0 -> 660 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/decimal_literal.cpython-35.pycbin0 -> 669 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/integer_literal.cpython-35.pycbin0 -> 669 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pycbin0 -> 946 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/money_literal.cpython-35.pycbin0 -> 663 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/string_literal.cpython-35.pycbin0 -> 666 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/__pycache__/undefined_literal.cpython-35.pycbin0 -> 675 bytes
-rw-r--r--ql/ast/nodes/expressions/literals/boolean_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/date_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/decimal_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/integer_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/literal.py14
-rw-r--r--ql/ast/nodes/expressions/literals/money_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/string_literal.py6
-rw-r--r--ql/ast/nodes/expressions/literals/undefined_literal.py6
-rw-r--r--ql/ast/nodes/expressions/unary_operators/__pycache__/negation_operator.cpython-35.pycbin0 -> 1283 bytes
-rw-r--r--ql/ast/nodes/expressions/unary_operators/__pycache__/negative_operator.cpython-35.pycbin0 -> 1402 bytes
-rw-r--r--ql/ast/nodes/expressions/unary_operators/__pycache__/unary_operator.cpython-35.pycbin0 -> 885 bytes
-rw-r--r--ql/ast/nodes/expressions/unary_operators/negation_operator.py20
-rw-r--r--ql/ast/nodes/expressions/unary_operators/negative_operator.py23
-rw-r--r--ql/ast/nodes/expressions/unary_operators/unary_operator.py11
-rw-r--r--ql/ast/nodes/expressions/variable.py11
-rw-r--r--ql/ast/nodes/statements/__pycache__/block_statement.cpython-35.pycbin0 -> 804 bytes
-rw-r--r--ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pycbin0 -> 820 bytes
-rw-r--r--ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pycbin0 -> 807 bytes
-rw-r--r--ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pycbin0 -> 1495 bytes
-rw-r--r--ql/ast/nodes/statements/block_statement.py11
-rw-r--r--ql/ast/nodes/statements/form_statement.py12
-rw-r--r--ql/ast/nodes/statements/if_statement.py11
-rw-r--r--ql/ast/nodes/statements/question_statement.py31
-rw-r--r--ql/ast/visitors/__pycache__/dependency_visitor.cpython-35.pycbin0 -> 2626 bytes
-rw-r--r--ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pycbin0 -> 1834 bytes
-rw-r--r--ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pycbin0 -> 1552 bytes
-rw-r--r--ql/ast/visitors/__pycache__/model_generator.cpython-35.pycbin0 -> 1918 bytes
-rw-r--r--ql/ast/visitors/__pycache__/question_visitor.cpython-35.pycbin0 -> 1555 bytes
-rw-r--r--ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pycbin0 -> 2997 bytes
-rw-r--r--ql/ast/visitors/__pycache__/type_visitor.cpython-35.pycbin0 -> 3387 bytes
-rw-r--r--ql/ast/visitors/dependency_visitor.py54
-rw-r--r--ql/ast/visitors/expression_evaluator.py37
-rw-r--r--ql/ast/visitors/identifier_type_visitor.py31
-rw-r--r--ql/ast/visitors/model_generator.py39
-rw-r--r--ql/ast/visitors/question_visitor.py33
-rw-r--r--ql/ast/visitors/reference_visitor.py86
-rw-r--r--ql/ast/visitors/type_visitor.py85
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
new file mode 100644
index 0000000..5be2eb2
--- /dev/null
+++ b/ql/ast/checkers/__pycache__/dependency_checker.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc b/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc
new file mode 100644
index 0000000..bb81f4a
--- /dev/null
+++ b/ql/ast/checkers/__pycache__/question_checker.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc b/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc
new file mode 100644
index 0000000..3bdfae5
--- /dev/null
+++ b/ql/ast/checkers/__pycache__/reference_checker.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..ceb2379
--- /dev/null
+++ b/ql/ast/extractors/__pycache__/extractor.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..d1f8e50
--- /dev/null
+++ b/ql/ast/nodes/__pycache__/base.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..84ede5c
--- /dev/null
+++ b/ql/ast/nodes/expressions/__pycache__/expression.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc b/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc
new file mode 100644
index 0000000..b4ff8dc
--- /dev/null
+++ b/ql/ast/nodes/expressions/__pycache__/variable.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..bbc8130
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/addition_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..f004296
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/and_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..18d3fc0
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/binary_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..9cd8e0a
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/division_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..5d48766
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/equals_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..888c85c
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_equals_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..be0e395
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/greater_than_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..b7e8155
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_equals_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..850a39c
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/less_than_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..ff0e1ab
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/multiplication_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..fe666cf
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/not_equals_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..0b81fc3
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/or_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..cd89879
--- /dev/null
+++ b/ql/ast/nodes/expressions/binary_operators/__pycache__/subtraction_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..a6d43b1
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/boolean_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..33abf57
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/date_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..145ab16
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/decimal_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..33f5c62
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/integer_literal.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc b/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc
new file mode 100644
index 0000000..98af8e6
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..9dbf5d0
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/money_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..dbb3f72
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/string_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..73ce02b
--- /dev/null
+++ b/ql/ast/nodes/expressions/literals/__pycache__/undefined_literal.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..9a6e409
--- /dev/null
+++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/negation_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..45ad495
--- /dev/null
+++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/negative_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..f72b4f0
--- /dev/null
+++ b/ql/ast/nodes/expressions/unary_operators/__pycache__/unary_operator.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..8ec170d
--- /dev/null
+++ b/ql/ast/nodes/statements/__pycache__/block_statement.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc
new file mode 100644
index 0000000..e10775a
--- /dev/null
+++ b/ql/ast/nodes/statements/__pycache__/form_statement.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc
new file mode 100644
index 0000000..e81cfc1
--- /dev/null
+++ b/ql/ast/nodes/statements/__pycache__/if_statement.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc b/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc
new file mode 100644
index 0000000..6b86ed9
--- /dev/null
+++ b/ql/ast/nodes/statements/__pycache__/question_statement.cpython-35.pyc
Binary files differ
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
new file mode 100644
index 0000000..8d348e6
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/dependency_visitor.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc b/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc
new file mode 100644
index 0000000..29a2e9b
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/expression_evaluator.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc
new file mode 100644
index 0000000..89f690d
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/identifier_type_visitor.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc b/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc
new file mode 100644
index 0000000..9950522
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/model_generator.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc
new file mode 100644
index 0000000..ac60a13
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/question_visitor.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc
new file mode 100644
index 0000000..e0491b9
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/reference_visitor.cpython-35.pyc
Binary files differ
diff --git a/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc b/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc
new file mode 100644
index 0000000..a2df3f2
--- /dev/null
+++ b/ql/ast/visitors/__pycache__/type_visitor.cpython-35.pyc
Binary files differ
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