cauldron 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README +1 -0
- data/README.rdoc +19 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/cauldron +10 -0
- data/cauldron/.autotest +23 -0
- data/cauldron/History.txt +6 -0
- data/cauldron/Manifest.txt +8 -0
- data/cauldron/README.txt +57 -0
- data/cauldron/Rakefile +27 -0
- data/cauldron/bin/cauldron +3 -0
- data/cauldron/lib/cauldron.rb +3 -0
- data/cauldron/test/test_cauldron.rb +8 -0
- data/features/cauldron_example_cases.feature +13 -0
- data/features/cauldron_generates_runtime_method.feature +12 -0
- data/features/cauldron_start_terminal.feature +13 -0
- data/features/step_definitions/cauldron_steps.rb +16 -0
- data/features/step_definitions/terminal_steps.rb +34 -0
- data/features/support/env.rb +19 -0
- data/lib/Chain.rb +879 -0
- data/lib/ChainMapping.rb +172 -0
- data/lib/CodeHandler.rb +517 -0
- data/lib/Mapping.rb +26 -0
- data/lib/MappingValues.rb +24 -0
- data/lib/ScopeDependencies.rb +7 -0
- data/lib/Theory.rb +274 -0
- data/lib/UnifiedChain.rb +110 -0
- data/lib/cauldron.rb +6 -0
- data/lib/cauldron/conversion.rb +15 -0
- data/lib/cauldron/pot.rb +134 -0
- data/lib/cauldron/sexp2cauldron.rb +156 -0
- data/lib/cauldron/terminal.rb +120 -0
- data/lib/cauldron/theory_factory.rb +10 -0
- data/lib/core/ActsAsCode.rb +25 -0
- data/lib/core/BlockToken.rb +33 -0
- data/lib/core/CCall.rb +7 -0
- data/lib/core/CTestCase.rb +27 -0
- data/lib/core/ClassMethodCallContainer.rb +45 -0
- data/lib/core/Container.rb +85 -0
- data/lib/core/InstanceCallContainer.rb +272 -0
- data/lib/core/MethodUsage.rb +66 -0
- data/lib/core/PrintVariables.rb +25 -0
- data/lib/core/TheoryGenerator.rb +764 -0
- data/lib/core/Token.rb +7 -0
- data/lib/core/assignment/Assignment.rb +18 -0
- data/lib/core/assignment/Equal.rb +39 -0
- data/lib/core/assignment/Equivalent.rb +20 -0
- data/lib/core/assignment/NotEqual.rb +14 -0
- data/lib/core/call_container/CallContainer.rb +66 -0
- data/lib/core/class_method_call/New.rb +15 -0
- data/lib/core/class_method_call/RuntimeClassMethodCall.rb +31 -0
- data/lib/core/declaration/Declaration.rb +16 -0
- data/lib/core/declaration/LiteralDeclaration.rb +47 -0
- data/lib/core/declaration/VariableDeclaration.rb +59 -0
- data/lib/core/instance_call/ArrayEach.rb +23 -0
- data/lib/core/instance_call/ArrayLength.rb +15 -0
- data/lib/core/instance_call/Chop.rb +28 -0
- data/lib/core/instance_call/Copy.rb +22 -0
- data/lib/core/instance_call/DeclaredVariable.rb +18 -0
- data/lib/core/instance_call/InstanceCall.rb +77 -0
- data/lib/core/instance_call/Params.rb +15 -0
- data/lib/core/instance_call/Push.rb +20 -0
- data/lib/core/instance_call/StringLength.rb +32 -0
- data/lib/core/instance_call/Times.rb +20 -0
- data/lib/core/instance_call/instance_calls.rb +168 -0
- data/lib/core/instance_call/length_equal.rb +15 -0
- data/lib/core/kernal/EvalCall.rb +15 -0
- data/lib/core/kernal/LocalVariablesCall.rb +15 -0
- data/lib/core/literal/Literal.rb +111 -0
- data/lib/core/literal/Raw.rb +23 -0
- data/lib/core/literal/RuntimeMethodLiteral.rb +21 -0
- data/lib/core/literal/StatementLiteral.rb +28 -0
- data/lib/core/method_call/AvailableVariablesCall.rb +25 -0
- data/lib/core/method_call/ClassCall.rb +33 -0
- data/lib/core/method_call/DefCall.rb +72 -0
- data/lib/core/method_call/EvaluateClassCall.rb +29 -0
- data/lib/core/method_call/MethodNameCall.rb +27 -0
- data/lib/core/method_call/ToDeclarationCall.rb +15 -0
- data/lib/core/requirement/Requirement.rb +291 -0
- data/lib/core/runtime_class/ArrayClass.rb +19 -0
- data/lib/core/runtime_class/ClassCallClass.rb +23 -0
- data/lib/core/runtime_class/ClassEvaluationClass.rb +19 -0
- data/lib/core/runtime_class/ClassName.rb +18 -0
- data/lib/core/runtime_class/DefCallClass.rb +21 -0
- data/lib/core/runtime_class/EqualClass.rb +19 -0
- data/lib/core/runtime_class/FixnumClass.rb +15 -0
- data/lib/core/runtime_class/IfStatementClass.rb +12 -0
- data/lib/core/runtime_class/InstanceCallClass.rb +19 -0
- data/lib/core/runtime_class/InstanceCallContainerClass.rb +16 -0
- data/lib/core/runtime_class/InstanceClassCallClass.rb +19 -0
- data/lib/core/runtime_class/LiteralClass.rb +19 -0
- data/lib/core/runtime_class/MethodParameterClass.rb +27 -0
- data/lib/core/runtime_class/MethodUsageClass.rb +27 -0
- data/lib/core/runtime_class/RequirementClass.rb +19 -0
- data/lib/core/runtime_class/ReturnClass.rb +21 -0
- data/lib/core/runtime_class/RuntimeClass.rb +30 -0
- data/lib/core/runtime_class/RuntimeClassClass.rb +19 -0
- data/lib/core/runtime_class/RuntimeMethodClass.rb +34 -0
- data/lib/core/runtime_class/StatementClass.rb +53 -0
- data/lib/core/runtime_class/StringClass.rb +23 -0
- data/lib/core/runtime_class/StringLengthClass.rb +19 -0
- data/lib/core/runtime_class/StringVariableClass.rb +19 -0
- data/lib/core/runtime_class/ThisClass.rb +15 -0
- data/lib/core/runtime_class/UnknownClass.rb +23 -0
- data/lib/core/runtime_class/class_names.rb +95 -0
- data/lib/core/runtime_class/runtime_class.rb +123 -0
- data/lib/core/runtime_method/ActsAsRuntimeMethod.rb +300 -0
- data/lib/core/runtime_method/ParametersContainer.rb +29 -0
- data/lib/core/runtime_method/RealisedRuntimeMethod.rb +94 -0
- data/lib/core/runtime_method/RuntimeMethod.rb +817 -0
- data/lib/core/runtime_method/WriteParameters.rb +35 -0
- data/lib/core/statement/ActsAsStatement.rb +116 -0
- data/lib/core/statement/ArrayAccess.rb +96 -0
- data/lib/core/statement/BlockStatement.rb +348 -0
- data/lib/core/statement/DeclarationStatement.rb +19 -0
- data/lib/core/statement/HackStatement.rb +25 -0
- data/lib/core/statement/HashAccess.rb +18 -0
- data/lib/core/statement/OpenStatement.rb +171 -0
- data/lib/core/statement/RealisedStatement.rb +5 -0
- data/lib/core/statement/SingleLineBlockStatement.rb +39 -0
- data/lib/core/statement/Statement.rb +1223 -0
- data/lib/core/statement/StatementDependencies.rb +271 -0
- data/lib/core/statement/StatementGroup.rb +157 -0
- data/lib/core/statement/StatementStructure2.rb +224 -0
- data/lib/core/statement/TheoryStatement.rb +60 -0
- data/lib/core/statement/TopologicalStatements.rb +34 -0
- data/lib/core/structure/DeclareNewInstanceStructure.rb +49 -0
- data/lib/core/structure/DeclareRuntimeMethodStructure.rb +34 -0
- data/lib/core/structure/DeclareVariableAsLiteralStructure.rb +31 -0
- data/lib/core/structure/DeclareVariableAsVariableStructure.rb +52 -0
- data/lib/core/structure/FixnumAdditionStructure.rb +56 -0
- data/lib/core/structure/InstanceCallContainerStructure.rb +50 -0
- data/lib/core/structure/InstanceCallStructure.rb +53 -0
- data/lib/core/structure/InstanceMethodCallStructure.rb +21 -0
- data/lib/core/structure/ReturnStructure.rb +20 -0
- data/lib/core/structure/StatementStructure.rb +11 -0
- data/lib/core/syntax/Addition.rb +25 -0
- data/lib/core/syntax/BlockContainer.rb +130 -0
- data/lib/core/syntax/Boolean.rb +15 -0
- data/lib/core/syntax/Code.rb +11 -0
- data/lib/core/syntax/Do.rb +20 -0
- data/lib/core/syntax/False.rb +12 -0
- data/lib/core/syntax/If.rb +28 -0
- data/lib/core/syntax/IfContainer.rb +100 -0
- data/lib/core/syntax/Nil.rb +15 -0
- data/lib/core/syntax/Return.rb +33 -0
- data/lib/core/syntax/Subtract.rb +19 -0
- data/lib/core/syntax/This.rb +40 -0
- data/lib/core/syntax/True.rb +20 -0
- data/lib/core/syntax/syntax.rb +24 -0
- data/lib/core/tracking/ActsAsTrackable.rb +65 -0
- data/lib/core/tracking/History.rb +167 -0
- data/lib/core/tracking/RuntimeTrackingMethod.rb +32 -0
- data/lib/core/tracking/Step.rb +52 -0
- data/lib/core/variable/ArrayVariable.rb +76 -0
- data/lib/core/variable/BaseVariable.rb +154 -0
- data/lib/core/variable/BlockVariable.rb +92 -0
- data/lib/core/variable/FixnumVariable.rb +36 -0
- data/lib/core/variable/HistoryVariable.rb +8 -0
- data/lib/core/variable/MethodParameter.rb +206 -0
- data/lib/core/variable/MethodUsageVariable.rb +60 -0
- data/lib/core/variable/NilVariable.rb +29 -0
- data/lib/core/variable/RuntimeMethodParameter.rb +67 -0
- data/lib/core/variable/StatementVariable.rb +72 -0
- data/lib/core/variable/StepVariable.rb +7 -0
- data/lib/core/variable/StringVariable.rb +46 -0
- data/lib/core/variable/TypeVariable.rb +72 -0
- data/lib/core/variable/Unknown.rb +116 -0
- data/lib/core/variable/UnknownVariable.rb +29 -0
- data/lib/core/variable/Variable.rb +70 -0
- data/lib/core/variable/VariableContainer.rb +28 -0
- data/lib/core/variable/VariableIncluded.rb +27 -0
- data/lib/core/variable/VariableReference.rb +85 -0
- data/lib/error/FailedToFindStatementContainerError.rb +7 -0
- data/lib/error/FailedToFindStatementError.rb +7 -0
- data/lib/error/FailedToFindVariableError.rb +7 -0
- data/lib/error/FailedToLiteraliseError.rb +7 -0
- data/lib/error/FailedVariableMatch.rb +7 -0
- data/lib/error/ImproperStatementUsageError.rb +7 -0
- data/lib/error/IncompatiableRequirementsError.rb +7 -0
- data/lib/error/InvalidStatementError.rb +7 -0
- data/lib/error/MethodSizeError.rb +7 -0
- data/lib/error/RuntimeSyntaxError.rb +7 -0
- data/lib/error/UnexpectedStatementTypeError.rb +7 -0
- data/lib/error/UnknownStatementType.rb +7 -0
- data/lib/error/UnliteralisableError.rb +7 -0
- data/lib/implemented_chain.rb +34 -0
- data/lib/intrinsic/IntrinsicLastRuntimeMethod.rb +20 -0
- data/lib/intrinsic/IntrinsicLiteral.rb +26 -0
- data/lib/intrinsic/IntrinsicObject.rb +22 -0
- data/lib/intrinsic/IntrinsicRuntimeMethod.rb +27 -0
- data/lib/intrinsic/IntrinsicTestCases.rb +17 -0
- data/lib/logger/StandardLogger.rb +62 -0
- data/lib/required.rb +236 -0
- data/lib/ruby_code/Array.rb +95 -0
- data/lib/ruby_code/Fixnum.rb +39 -0
- data/lib/ruby_code/Hash.rb +25 -0
- data/lib/ruby_code/NilClass.rb +19 -0
- data/lib/ruby_code/Object.rb +24 -0
- data/lib/ruby_code/String.rb +86 -0
- data/lib/ruby_code/Symbol.rb +7 -0
- data/lib/standard_library/Tasks.rb +12 -0
- data/lib/theories.rb +143 -0
- data/lib/theory/ActionImplementation.rb +17 -0
- data/lib/theory/TheoryAction.rb +70 -0
- data/lib/theory/TheoryChainValidator.rb +101 -0
- data/lib/theory/TheoryComponent.rb +42 -0
- data/lib/theory/TheoryConnector.rb +755 -0
- data/lib/theory/TheoryDependent.rb +135 -0
- data/lib/theory/TheoryImplementation.rb +74 -0
- data/lib/theory/TheoryResult.rb +131 -0
- data/lib/theory/TheoryVariable.rb +63 -0
- data/lib/theory/theory_collection.rb +62 -0
- data/lib/util/ClassEvaluation.rb +68 -0
- data/lib/util/CodeEvaluation.rb +35 -0
- data/lib/util/DeclarationStatementEvaluation.rb +31 -0
- data/lib/util/MethodEvaluation.rb +49 -0
- data/lib/util/MethodTester.rb +71 -0
- data/lib/util/MethodValidation.rb +145 -0
- data/lib/util/MethodWriter.rb +66 -0
- data/lib/util/Parser.rb +299 -0
- data/lib/util/StatementCheck.rb +42 -0
- data/lib/util/StringToTheory.rb +119 -0
- data/lib/util/System.rb +8 -0
- data/spec/cauldron/pot_spec.rb +6 -0
- data/spec/cauldron/runtime_method_spec.rb +36 -0
- data/spec/cauldron/sexp_2_cauldron_spec.rb +26 -0
- data/spec/cauldron/terminal_spec.rb +38 -0
- data/spec/cauldron/theory_action_spec.rb +5 -0
- data/spec/spec_helper.rb +4 -0
- data/test/fixtures/chains/1/declaration.txt +26 -0
- data/test/fixtures/chains/1/dump +0 -0
- data/test/fixtures/chains/2/declaration.txt +26 -0
- data/test/fixtures/chains/2/dump +0 -0
- data/test/fixtures/chains/3/declaration.txt +26 -0
- data/test/fixtures/chains/3/dump +0 -0
- data/test/fixtures/implementation_results/0/declaration.txt +3 -0
- data/test/fixtures/implementation_results/0/dump +0 -0
- data/test/fixtures/theories/0/declaration.txt +9 -0
- data/test/fixtures/theories/0/desc +10 -0
- data/test/fixtures/theories/0/dump +0 -0
- data/test/fixtures/theories/1/declaration.txt +15 -0
- data/test/fixtures/theories/1/desc +11 -0
- data/test/fixtures/theories/1/dump +0 -0
- data/test/fixtures/theories/10/declaration.txt +23 -0
- data/test/fixtures/theories/10/desc +17 -0
- data/test/fixtures/theories/10/dump +0 -0
- data/test/fixtures/theories/11/declaration.txt +20 -0
- data/test/fixtures/theories/11/desc +14 -0
- data/test/fixtures/theories/11/dump +0 -0
- data/test/fixtures/theories/12/declaration.txt +18 -0
- data/test/fixtures/theories/12/desc +12 -0
- data/test/fixtures/theories/12/dump +0 -0
- data/test/fixtures/theories/13/declaration.txt +24 -0
- data/test/fixtures/theories/13/desc +20 -0
- data/test/fixtures/theories/13/dump +0 -0
- data/test/fixtures/theories/14/declaration.txt +26 -0
- data/test/fixtures/theories/14/desc +20 -0
- data/test/fixtures/theories/14/dump +0 -0
- data/test/fixtures/theories/15/declaration.txt +20 -0
- data/test/fixtures/theories/15/desc +14 -0
- data/test/fixtures/theories/15/dump +0 -0
- data/test/fixtures/theories/16/declaration.txt +30 -0
- data/test/fixtures/theories/16/desc +14 -0
- data/test/fixtures/theories/16/dump +0 -0
- data/test/fixtures/theories/17/declaration.txt +25 -0
- data/test/fixtures/theories/17/desc +11 -0
- data/test/fixtures/theories/17/dump +0 -0
- data/test/fixtures/theories/18/declaration.txt +23 -0
- data/test/fixtures/theories/18/desc +11 -0
- data/test/fixtures/theories/18/dump +0 -0
- data/test/fixtures/theories/19/declaration.txt +23 -0
- data/test/fixtures/theories/19/desc +11 -0
- data/test/fixtures/theories/19/dump +0 -0
- data/test/fixtures/theories/2/declaration.txt +12 -0
- data/test/fixtures/theories/2/desc +10 -0
- data/test/fixtures/theories/2/dump +0 -0
- data/test/fixtures/theories/20/declaration.txt +23 -0
- data/test/fixtures/theories/20/desc +17 -0
- data/test/fixtures/theories/20/dump +0 -0
- data/test/fixtures/theories/3/declaration.txt +19 -0
- data/test/fixtures/theories/3/desc +11 -0
- data/test/fixtures/theories/3/dump +0 -0
- data/test/fixtures/theories/4/declaration.txt +11 -0
- data/test/fixtures/theories/4/desc +11 -0
- data/test/fixtures/theories/4/dump +0 -0
- data/test/fixtures/theories/5/declaration.txt +6 -0
- data/test/fixtures/theories/5/desc +9 -0
- data/test/fixtures/theories/5/dump +0 -0
- data/test/fixtures/theories/6/declaration.txt +13 -0
- data/test/fixtures/theories/6/desc +11 -0
- data/test/fixtures/theories/6/dump +0 -0
- data/test/fixtures/theories/7/declaration.txt +19 -0
- data/test/fixtures/theories/7/desc +11 -0
- data/test/fixtures/theories/7/dump +0 -0
- data/test/fixtures/theories/8/declaration.txt +21 -0
- data/test/fixtures/theories/8/desc +11 -0
- data/test/fixtures/theories/8/dump +0 -0
- data/test/fixtures/theories/9/declaration.txt +24 -0
- data/test/fixtures/theories/9/desc +20 -0
- data/test/fixtures/theories/9/dump +0 -0
- data/test/fixtures/theory_implementations/0/declaration.txt +11 -0
- data/test/fixtures/theory_implementations/0/desc.txt +9 -0
- data/test/fixtures/theory_implementations/0/dump +0 -0
- data/test/fixtures/theory_implementations/0/theory_id +1 -0
- data/test/fixtures/theory_implementations/1/desc.txt +9 -0
- data/test/fixtures/theory_implementations/1/dump +0 -0
- data/test/fixtures/theory_implementations/1/theory_id +1 -0
- data/test/fixtures/theory_implementations/2/desc.txt +9 -0
- data/test/fixtures/theory_implementations/2/dump +0 -0
- data/test/fixtures/theory_implementations/2/theory_id +1 -0
- data/test/output/simple_method.txt +6 -0
- data/test/output/test_method/first_possible_method.txt +6 -0
- data/test/output/test_simple_cases/simple_case_01.txt +8 -0
- data/test/output/test_simple_cases/simple_case_02.txt +7 -0
- data/test/output/test_simple_cases/simple_case_03.txt +8 -0
- data/test/output/test_simple_cases/simple_case_04.txt +8 -0
- data/test/output/test_simple_cases/simple_case_05.txt +8 -0
- data/test/output/test_simple_cases/simple_case_06.txt +9 -0
- data/test/output/test_simple_cases/simple_case_07.txt +9 -0
- data/test/output/test_simple_cases/simple_case_08.txt +9 -0
- data/test/tc_contextual_variables.rb +87 -0
- data/test/tc_describe.rb +47 -0
- data/test/tc_method.rb +133 -0
- data/test/tc_requirement.rb +30 -0
- data/test/tc_suite_complete.rb +26 -0
- data/test/tc_variable.rb +52 -0
- data/test/ts_complete.rb +84 -0
- data/test/ts_stable.rb +81 -0
- data/test/unit/core/declaration/tc_literal_declaration.rb +34 -0
- data/test/unit/core/method_call/tc_class_call.rb +20 -0
- data/test/unit/core/runtime_method/tc_realised_runtime_method.rb +129 -0
- data/test/unit/core/runtime_method/tc_runtime_method.rb +616 -0
- data/test/unit/core/statement/tc_array_access.rb +63 -0
- data/test/unit/core/statement/tc_block_statement.rb +51 -0
- data/test/unit/core/statement/tc_hack_statement.rb +26 -0
- data/test/unit/core/statement/tc_open_statement.rb +70 -0
- data/test/unit/core/statement/tc_statement.rb +681 -0
- data/test/unit/core/statement/tc_statement_dependencies.rb +146 -0
- data/test/unit/core/statement/tc_statement_group.rb +35 -0
- data/test/unit/core/statement/tc_statement_replace_variable.rb +61 -0
- data/test/unit/core/statement/tc_theory_statement.rb +51 -0
- data/test/unit/core/structure/tc_declare_new_instance_structure.rb +41 -0
- data/test/unit/core/structure/tc_declare_variable_as_literal_structure.rb +41 -0
- data/test/unit/core/structure/tc_declare_variable_as_variable_structure.rb +66 -0
- data/test/unit/core/structure/tc_instance_call_container_structure.rb +41 -0
- data/test/unit/core/structure/tc_return_structure.rb +32 -0
- data/test/unit/core/syntax/tc_block_container.rb +32 -0
- data/test/unit/core/syntax/tc_if_container.rb +39 -0
- data/test/unit/core/tc_class_method_call.rb +34 -0
- data/test/unit/core/tc_container.rb +41 -0
- data/test/unit/core/tc_ctest_case.rb +25 -0
- data/test/unit/core/tc_instance_call_container.rb +93 -0
- data/test/unit/core/tc_literal.rb +30 -0
- data/test/unit/core/tc_theory_generator.rb +336 -0
- data/test/unit/core/tc_theory_generator_heavy.rb +42 -0
- data/test/unit/core/tracking/tc_history.rb +102 -0
- data/test/unit/core/tracking/tc_step.rb +65 -0
- data/test/unit/core/variable/tc_array_variable.rb +61 -0
- data/test/unit/core/variable/tc_block_variable.rb +17 -0
- data/test/unit/core/variable/tc_fixnum_variable.rb +54 -0
- data/test/unit/core/variable/tc_method_parameter_variable.rb +22 -0
- data/test/unit/core/variable/tc_runtime_method_variable.rb +32 -0
- data/test/unit/core/variable/tc_string_variable.rb +37 -0
- data/test/unit/core/variable/tc_unknown.rb +24 -0
- data/test/unit/core/variable/tc_variable_reference.rb +28 -0
- data/test/unit/ruby_code/tc_array.rb +64 -0
- data/test/unit/ruby_code/tc_fixnum.rb +18 -0
- data/test/unit/ruby_code/tc_hash.rb +41 -0
- data/test/unit/ruby_code/tc_string.rb +38 -0
- data/test/unit/tc_chain.rb +434 -0
- data/test/unit/tc_chain_mapping.rb +62 -0
- data/test/unit/tc_chain_with_case_1.rb +169 -0
- data/test/unit/tc_instance_call.rb +40 -0
- data/test/unit/tc_instance_call_structure.rb +35 -0
- data/test/unit/tc_method_usage.rb +35 -0
- data/test/unit/tc_pot.rb +124 -0
- data/test/unit/tc_runtime_tracking_method.rb +40 -0
- data/test/unit/tc_statement_structure_2.rb +43 -0
- data/test/unit/tc_theory.rb +533 -0
- data/test/unit/tc_variable_declaration.rb +32 -0
- data/test/unit/theory/tc_theory_action.rb +80 -0
- data/test/unit/theory/tc_theory_action_implementation.rb +23 -0
- data/test/unit/theory/tc_theory_chain_validator.rb +340 -0
- data/test/unit/theory/tc_theory_connector.rb +396 -0
- data/test/unit/theory/tc_theory_dependent.rb +151 -0
- data/test/unit/theory/tc_theory_implementation.rb +133 -0
- data/test/unit/theory/tc_theory_result.rb +111 -0
- data/test/unit/theory/tc_theory_variable.rb +45 -0
- data/test/unit/util/tc_method_validation.rb +98 -0
- data/test/unit/util/tc_parser.rb +108 -0
- data/test/unit/util/tc_string_to_theory.rb +299 -0
- data/test/unit/variable/tc_method_usage_variable.rb +25 -0
- data/tmp/runtime_method_evaluation.rb +10 -0
- metadata +522 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# This class represents statements that declare new variables.
|
2
|
+
# e.g. var = 'test'.chop
|
3
|
+
# var1 = var2.pop
|
4
|
+
#
|
5
|
+
class DeclarationStatement < Statement
|
6
|
+
|
7
|
+
def initialize(*parameters)
|
8
|
+
super(*parameters)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the declared variable. So in he case where
|
12
|
+
# there is var = 8 it would return var. This is always
|
13
|
+
# the first entry.
|
14
|
+
#
|
15
|
+
def declared
|
16
|
+
return self.first
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This is a temporary statement that simple writes out the string provided to it. It
|
2
|
+
# is used by the RuntimeTrackingMethod and is simple used now because I am not
|
3
|
+
# able to create hashes just yet. I hope to remove this class in the foreseable
|
4
|
+
# future(15/07/09) - lets see.
|
5
|
+
#
|
6
|
+
class HackStatement < Statement
|
7
|
+
|
8
|
+
def initialize(statement_text)
|
9
|
+
super()
|
10
|
+
@statement_text = statement_text
|
11
|
+
@confirmed = true
|
12
|
+
@statement_type = 'Hack'
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(tab=0)
|
16
|
+
l = ''
|
17
|
+
tab.times {|x| l += "\t" }
|
18
|
+
return l+@statement_text
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy
|
22
|
+
return HackStatement.new(@statement_text.dup)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class HashAccess
|
2
|
+
|
3
|
+
attr_reader :hash, :key
|
4
|
+
|
5
|
+
def initialize(array,index)
|
6
|
+
@hash = array
|
7
|
+
@key = index
|
8
|
+
end
|
9
|
+
|
10
|
+
def write
|
11
|
+
return @hash.write+'['+@key.write+']'
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy
|
15
|
+
return HashAccess.new(@hash.copy,@key.copy)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# This class represents the structure where a statement is declared
|
2
|
+
# but doesn't close on the same line. Instead it can contain any
|
3
|
+
# number of other statements.
|
4
|
+
#
|
5
|
+
# for example
|
6
|
+
#
|
7
|
+
# if var_a == 'test'.select_all {|x| x.kind_of?(TheoryVariable)}
|
8
|
+
# # do something
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
class OpenStatement < StatementGroup
|
12
|
+
include ActsAsTrackable
|
13
|
+
include ActsAsCode
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(open_statement,*statements)
|
17
|
+
super(*statements)
|
18
|
+
@statement = open_statement
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(tab=0)
|
22
|
+
line = @statement.write(tab)+"\n"
|
23
|
+
line += super(tab+1)
|
24
|
+
tab.times {line += "\t" }
|
25
|
+
line += "end"
|
26
|
+
return line
|
27
|
+
end
|
28
|
+
|
29
|
+
def describe(tab=0)
|
30
|
+
line = @statement.describe(tab)+"\n"
|
31
|
+
line += super(tab+1)
|
32
|
+
tab.times {line += "\t" }
|
33
|
+
line += "end\n"
|
34
|
+
return line
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a copy of the current nested statement but without any statements
|
38
|
+
#
|
39
|
+
def copy_base
|
40
|
+
result = self.class.new(@statement.copy)
|
41
|
+
# TODO This means the scope id is being incremented for statements only exist for the declaration
|
42
|
+
result.scope_id = scope_id
|
43
|
+
return result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns all the variables used in the opening statement as well as any
|
47
|
+
# child statements.
|
48
|
+
#
|
49
|
+
# TODO Write tests for this
|
50
|
+
def variables
|
51
|
+
return self.select_all([]){|x| x.kind_of?(Variable)}
|
52
|
+
end
|
53
|
+
|
54
|
+
def tokens
|
55
|
+
return self.select_all([]){|x| x.kind_of?(Token)}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Overrides the default array select_all method to include
|
59
|
+
# the opening statement and contents of the open statement.
|
60
|
+
#
|
61
|
+
def select_all(results=[],&block)
|
62
|
+
@statement.select_all(results,&block)
|
63
|
+
return super(results,&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Create a duplicate of this OpenStatement instance
|
67
|
+
#
|
68
|
+
# TODO Write tests for this
|
69
|
+
#
|
70
|
+
def copy
|
71
|
+
result = self.class.new(@statement.copy,*self.collect {|x| x.copy })
|
72
|
+
result.scope_id = scope_id
|
73
|
+
result.scope = scope
|
74
|
+
result.statement_level = statement_level
|
75
|
+
return result
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a new open statement that has been trackified. A
|
79
|
+
# trackified open statement has a statement call after each
|
80
|
+
# line.
|
81
|
+
#
|
82
|
+
# @param tracking_method A runtime tracking method instance
|
83
|
+
# @param line An array thats length represents the current line.
|
84
|
+
#
|
85
|
+
def trackify(tracking_method,line)
|
86
|
+
|
87
|
+
# First copy the base open statement and repopulate it
|
88
|
+
result = copy_base
|
89
|
+
|
90
|
+
# Start by trackifying the opening statement
|
91
|
+
opening_variables = abstract_variables_for_tracking(@statement)
|
92
|
+
track_statement = tracking_statement(
|
93
|
+
tracking_method,
|
94
|
+
line,
|
95
|
+
statement_id.to_literal,
|
96
|
+
opening_variables,
|
97
|
+
@statement
|
98
|
+
)
|
99
|
+
result.push(track_statement)
|
100
|
+
|
101
|
+
# For each line in the open statement include a tracking call
|
102
|
+
# TODO I don't think this would trackify further open statements
|
103
|
+
self.each do |x|
|
104
|
+
result.push(x.copy)
|
105
|
+
|
106
|
+
# Now include a tracking call for after the statement
|
107
|
+
line_variables = abstract_variables_for_tracking(x)
|
108
|
+
# TODO Check that the line is going up
|
109
|
+
result.push tracking_statement(
|
110
|
+
tracking_method,
|
111
|
+
line,
|
112
|
+
x.statement_id,
|
113
|
+
line_variables,
|
114
|
+
x
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
return result
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def replace_theory_variables!(mapping)
|
123
|
+
@statement.replace_theory_variables!(mapping)
|
124
|
+
self.each_with_index do |x,i|
|
125
|
+
if x.kind_of?(TheoryVariable) && mapping.has_key?(x.theory_variable_id)
|
126
|
+
self[i] = mapping[x.theory_variable_id].copy
|
127
|
+
next
|
128
|
+
end
|
129
|
+
self[i].respond_to?(:replace_theory_variables!)
|
130
|
+
self[i].replace_theory_variables!(mapping) if self[i].respond_to?(:replace_theory_variables!)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns the statement_id of the embeded statement.
|
135
|
+
#
|
136
|
+
# TODO The nested statement should maybe have an idea
|
137
|
+
# of its own. It should probably have two ids
|
138
|
+
# to indicate at what point additional statements
|
139
|
+
# added e.g. inside/outside statement.
|
140
|
+
#
|
141
|
+
def statement_id
|
142
|
+
return @statement.statement_id
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO This seems wrong there is no opening statement
|
146
|
+
def to_literal_string
|
147
|
+
a = ''
|
148
|
+
self.each do |x|
|
149
|
+
a += x.to_literal_string
|
150
|
+
end
|
151
|
+
return a
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns true if the opening statement and all internal statements
|
155
|
+
# have been realised.
|
156
|
+
#
|
157
|
+
def realised?
|
158
|
+
return false unless @statement.realised?
|
159
|
+
return false unless self.all? {|x| x.realised? }
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
|
163
|
+
def has?(&block)
|
164
|
+
self.each do |x|
|
165
|
+
return true if block.call(x)
|
166
|
+
end
|
167
|
+
return true if block.call(@statement)
|
168
|
+
return false
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# TODO It is probably a bit overkill to create a new class for this.
|
2
|
+
#
|
3
|
+
# This is the same as the block statement except it uses the curly bracket syntax
|
4
|
+
#
|
5
|
+
class SingleLineBlockStatement < BlockStatement
|
6
|
+
|
7
|
+
def initialize(statement,block_container=BlockContainer.new(BlockVariable.new),*internal_statements)
|
8
|
+
super(statement,block_container,*internal_statements)
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(tab=0)
|
12
|
+
l = @statement.write(tab)+'{ '+@block_container.write+' '
|
13
|
+
self.each do |x|
|
14
|
+
l += x.write
|
15
|
+
end
|
16
|
+
l += '} '
|
17
|
+
return l
|
18
|
+
end
|
19
|
+
|
20
|
+
def describe(tab=0)
|
21
|
+
l = @statement.describe(tab)+'{ '+@block_container.describe+' '
|
22
|
+
self.each do |x|
|
23
|
+
l += x.describe
|
24
|
+
end
|
25
|
+
l += '} '
|
26
|
+
return l
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy
|
30
|
+
|
31
|
+
result = SingleLineBlockStatement.new(@statement.copy,@block_container.copy,*self.collect {|x| x.copy})
|
32
|
+
# TODO This means the scope id is being incremented for statements only exist for the declaration
|
33
|
+
result.scope_id = scope_id
|
34
|
+
result.scope = scope.copy
|
35
|
+
result.statement_level = statement_level
|
36
|
+
return result
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,1223 @@
|
|
1
|
+
class Statement < Array
|
2
|
+
include PrintVariables
|
3
|
+
include ActsAsStatement
|
4
|
+
include ActsAsCode
|
5
|
+
|
6
|
+
attr_reader :statement_id, :scope, :statement_level, :statement_id
|
7
|
+
# TODO scope and statement level are confusing - not sure of the difference (I think it's legacy)
|
8
|
+
attr_writer :scope, :statement_level, :overrides, :statement_id
|
9
|
+
|
10
|
+
alias :array_push :push
|
11
|
+
|
12
|
+
@@statement_id = 0
|
13
|
+
|
14
|
+
#
|
15
|
+
# @params parameters Any number of elements to make up the statement possible classes
|
16
|
+
# include Equal, Addition, Unknown, StringVariable, Literal e.t.c
|
17
|
+
# &block [OPTIONAL] A block can also be provided to preview the statement from being
|
18
|
+
# reviewed and therefore preventing changes to variables uniq_id.
|
19
|
+
#
|
20
|
+
def initialize(*parameters)
|
21
|
+
super()
|
22
|
+
|
23
|
+
#
|
24
|
+
@overrides = nil
|
25
|
+
@confirmed = false
|
26
|
+
|
27
|
+
# Sets the flag that indicates the structure of the statement
|
28
|
+
@structure = nil
|
29
|
+
|
30
|
+
# Add the parameters to the array
|
31
|
+
parameters.each do |code|
|
32
|
+
self.push(code.copy)
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO I might change the statement_id to be determined by the structure
|
36
|
+
# of the statement.
|
37
|
+
@statement_id = @@statement_id
|
38
|
+
@@statement_id += 1
|
39
|
+
|
40
|
+
# Review completed statement
|
41
|
+
if block_given?
|
42
|
+
values = yield
|
43
|
+
review if (values[:review])
|
44
|
+
else
|
45
|
+
review
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Resets the globablly used value for statements id
|
51
|
+
#
|
52
|
+
def self.reset_global_id
|
53
|
+
#http://www.zenspider.com/Languages/Ruby/QuickRef.html
|
54
|
+
unless $".include?('test/unit.rb')
|
55
|
+
StandardLogger.log 'WARNING: Resetting variable id, this should only be done for tests'
|
56
|
+
end
|
57
|
+
@@statement_id = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
# If this statement declares a variable determine whether the
|
61
|
+
# the variable has been previously declared. If so mark this
|
62
|
+
# statement as an "overrides" statement otherwise flag it
|
63
|
+
# as not.
|
64
|
+
#
|
65
|
+
def identify_overriding_statements(already_declared=[])
|
66
|
+
[BaseVariable,Equal].zip(self) do |x,y|
|
67
|
+
return unless y.kind_of?(x)
|
68
|
+
end
|
69
|
+
already_declared.each do |x|
|
70
|
+
if x[:variable_id] == declared_variable_id
|
71
|
+
@overrides = true
|
72
|
+
return
|
73
|
+
end
|
74
|
+
end
|
75
|
+
already_declared.push({:variable_id => declared_variable_id})
|
76
|
+
@overrides = false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns this statement if it has the correct id
|
80
|
+
#
|
81
|
+
# @param id The id of the statement should exist
|
82
|
+
#
|
83
|
+
def find_statement(id)
|
84
|
+
if self.statement_id == id
|
85
|
+
return self
|
86
|
+
end
|
87
|
+
raise FailedToFindStatementError.new('Couldn\'t find statement with id '+id.to_s)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns true if the statement overrides an existing variable.
|
91
|
+
# So if a = 6 but a was already set to 4 then this would be
|
92
|
+
# an overriding statement.
|
93
|
+
#
|
94
|
+
def overrides?
|
95
|
+
return @overrides
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns an array of variables in this statement
|
99
|
+
#
|
100
|
+
def variables
|
101
|
+
return self.select_all([]){|x| x.kind_of?(Variable)}
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns an array of all the tokens in the statement. A token
|
105
|
+
# is any literal, variable, intrinsic runtime or intrinsic testcase
|
106
|
+
#
|
107
|
+
def tokens
|
108
|
+
return self.select_all([]){|x| x.kind_of?(Token)}
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
def each_variable
|
113
|
+
variables.each { |x| yield x }
|
114
|
+
end
|
115
|
+
|
116
|
+
# Attempt to convert the statement into a string without any references
|
117
|
+
# to variables where possible. So varA = varB.chop might become
|
118
|
+
# "varA = 'test'chop"
|
119
|
+
#
|
120
|
+
def literalise
|
121
|
+
|
122
|
+
# Go through each element in the statement and attempt to literalise it
|
123
|
+
line = ''
|
124
|
+
self.each do |code|
|
125
|
+
unless code.kind_of? Variable or code.kind_of? InstanceCallContainer
|
126
|
+
line += code.write
|
127
|
+
next
|
128
|
+
end
|
129
|
+
if code.kind_of?(Variable)
|
130
|
+
if code.literalisable?
|
131
|
+
literal = code.literalise
|
132
|
+
line += literal.write
|
133
|
+
next
|
134
|
+
end
|
135
|
+
line += code.write
|
136
|
+
elsif code.kind_of?(InstanceCallContainer)
|
137
|
+
if(code.subject.literalisable?)
|
138
|
+
literal = code.subject.literalise
|
139
|
+
line += literal.write+'.'+code.method_call.write
|
140
|
+
next
|
141
|
+
end
|
142
|
+
line += code.write
|
143
|
+
end
|
144
|
+
end
|
145
|
+
return line
|
146
|
+
end
|
147
|
+
|
148
|
+
# TODO Maybe use a opject to handle the output of the statement.
|
149
|
+
def write(tab=0)
|
150
|
+
|
151
|
+
# Check the file type of tab
|
152
|
+
raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum
|
153
|
+
|
154
|
+
line = ''
|
155
|
+
tab.times {line += "\t" }
|
156
|
+
self.each do |code|
|
157
|
+
line += code.write
|
158
|
+
break if code.object_id == self.last.object_id
|
159
|
+
line += ' '
|
160
|
+
end
|
161
|
+
return line
|
162
|
+
end
|
163
|
+
|
164
|
+
def describe(tab=0)
|
165
|
+
# Check the file type of tab
|
166
|
+
raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum
|
167
|
+
|
168
|
+
line = ''
|
169
|
+
tab.times {line += "\t" }
|
170
|
+
self.each do |code|
|
171
|
+
line += code.describe
|
172
|
+
break if code.object_id == self.last.object_id
|
173
|
+
line += ' '
|
174
|
+
end
|
175
|
+
return line
|
176
|
+
end
|
177
|
+
|
178
|
+
def write_with_uniq_id(tab=0)
|
179
|
+
# Check the file type of tab
|
180
|
+
raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum
|
181
|
+
|
182
|
+
line = ''
|
183
|
+
tab.times {line += "\t" }
|
184
|
+
self.each do |code|
|
185
|
+
# TODO Doesn't need a space on the last code element
|
186
|
+
#line += code.write_with_uniq_id+' '
|
187
|
+
if code.respond_to?('write_with_uniq_id')
|
188
|
+
line += code.write_with_uniq_id+' '
|
189
|
+
next
|
190
|
+
end
|
191
|
+
line += code.write+' '
|
192
|
+
end
|
193
|
+
return line
|
194
|
+
end
|
195
|
+
|
196
|
+
# NOTE This method should only be used on realised statements
|
197
|
+
def write_with_value(tab=0)
|
198
|
+
|
199
|
+
# Check the file type of tab
|
200
|
+
raise StandardError.new('Unexpexted class "'+tab.class.to_s+'" for Fixnum (number of tabs)') unless tab.kind_of? Fixnum
|
201
|
+
|
202
|
+
line = ''
|
203
|
+
tab.times {line += "\t" }
|
204
|
+
self.each do |code|
|
205
|
+
|
206
|
+
if code.kind_of?(Variable)
|
207
|
+
|
208
|
+
line += code.write+'('+code.value.write+')'
|
209
|
+
unless code == self.last
|
210
|
+
line += ' '
|
211
|
+
end
|
212
|
+
next
|
213
|
+
end
|
214
|
+
|
215
|
+
line += code.write
|
216
|
+
|
217
|
+
end
|
218
|
+
return line
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns a string containing the written version of the statement
|
223
|
+
# but any variables will have there id in brackets after them.
|
224
|
+
#
|
225
|
+
# TODO This method is duplicated with requirements
|
226
|
+
#
|
227
|
+
def write_with_variable_id(tab=0,context=self)
|
228
|
+
|
229
|
+
line = ''
|
230
|
+
tab.times {line += "\t" }
|
231
|
+
self.each do |x|
|
232
|
+
if x.kind_of? Variable
|
233
|
+
line += x.write+'('+x.variable_id.to_s+') '
|
234
|
+
next
|
235
|
+
end
|
236
|
+
if x.kind_of? InstanceCallContainer
|
237
|
+
line += x.write+'('+x.subject.variable_id.to_s+')'
|
238
|
+
next
|
239
|
+
end
|
240
|
+
line += x.write
|
241
|
+
end
|
242
|
+
return line
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
# Creates a copy of the statement. The copy's variables should have
|
248
|
+
# the same variable ids but should be different instances. When
|
249
|
+
# all the code elements are added the statement is ascessed and the
|
250
|
+
# the requirements updated approriately.
|
251
|
+
#
|
252
|
+
def copy
|
253
|
+
# tmp = self.collect{|x| x.copy}
|
254
|
+
# result = self.class.new(*tmp) {{:review=>false}}
|
255
|
+
# result.statement_level = statement_level
|
256
|
+
# return result
|
257
|
+
return Marshal.load(Marshal.dump(self))
|
258
|
+
end
|
259
|
+
|
260
|
+
# Returns true if the statement contains any calls to a runtime
|
261
|
+
# method.
|
262
|
+
#
|
263
|
+
def contains_method_call?
|
264
|
+
return self.any? {|x| x.kind_of?(DefCall)}
|
265
|
+
end
|
266
|
+
|
267
|
+
# Adds the new piece of code to the statement and update
|
268
|
+
# the variables requirements to accomodate any change.
|
269
|
+
#
|
270
|
+
def push(code)
|
271
|
+
|
272
|
+
# Add the new piece of code to the statement
|
273
|
+
array_push(code)
|
274
|
+
|
275
|
+
# Update the requirements to reflect the change
|
276
|
+
update
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
def add(element)
|
281
|
+
push(element)
|
282
|
+
end
|
283
|
+
|
284
|
+
# Indicates whether the statement is one that assigns
|
285
|
+
# a value to another. Essential "Does the statement
|
286
|
+
# contain an equals sign?"
|
287
|
+
#
|
288
|
+
def assignment?
|
289
|
+
if detect_class(Equivalent).nil?
|
290
|
+
return false
|
291
|
+
end
|
292
|
+
return true
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
def right_hand_side
|
297
|
+
l, r = self.split(Equivalent)
|
298
|
+
return r
|
299
|
+
end
|
300
|
+
|
301
|
+
def left_hand_side
|
302
|
+
l, r = self.split(Equivalent)
|
303
|
+
return l
|
304
|
+
end
|
305
|
+
|
306
|
+
# Indicates whether the statement has been confirmed.
|
307
|
+
#
|
308
|
+
def confirmed?
|
309
|
+
return @confirmed
|
310
|
+
end
|
311
|
+
|
312
|
+
# This writes and evalutes the syntax of a statement to
|
313
|
+
# determine whether it can be used by it self.
|
314
|
+
#
|
315
|
+
# So something like var1 = var0.chop would fail since
|
316
|
+
# var0 doesn't exist. It needs to write to a different
|
317
|
+
# class to avoid the situation where the statement
|
318
|
+
# 'return false' would perceived as invalid syntax.
|
319
|
+
#
|
320
|
+
def valid_syntax?
|
321
|
+
return StatementCheck.new.valid_syntax?(self.write)
|
322
|
+
end
|
323
|
+
|
324
|
+
# yeilds each variable that is not declared in the statement and
|
325
|
+
# isn't known e.g. cat be literalised in the statement.
|
326
|
+
#
|
327
|
+
def each_untyped_variable
|
328
|
+
untyped_variables.each do |x|
|
329
|
+
yield x
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# Returns an array of all the untyped variables in the statement.
|
334
|
+
# Excluding any declared variables in the statement.
|
335
|
+
#
|
336
|
+
def untyped_variables
|
337
|
+
x = []
|
338
|
+
not_declared_variables.each do |y|
|
339
|
+
x.push(y) unless y.kind_of?(TypeVariable)
|
340
|
+
end
|
341
|
+
return x
|
342
|
+
end
|
343
|
+
|
344
|
+
# Returns each of the variables that aren't realised. In that
|
345
|
+
# they aren't TypeVariables and don't have a literal value.
|
346
|
+
#
|
347
|
+
def unrealised_variables
|
348
|
+
results = []
|
349
|
+
variables.each do |x|
|
350
|
+
unless x.kind_of?(TypeVariable)
|
351
|
+
results.push(x)
|
352
|
+
next
|
353
|
+
end
|
354
|
+
if x.value.nil? and !x.kind_of?(NilVariable)
|
355
|
+
results.push(x)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
return results
|
359
|
+
end
|
360
|
+
|
361
|
+
# Displays each of the unrealised variables within the statement
|
362
|
+
#
|
363
|
+
def each_unrealised_variable
|
364
|
+
unrealised_variables.each do |x|
|
365
|
+
yield x
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns an array of all the undeclared variables in the
|
370
|
+
# statement. An undeclared variable is simply a
|
371
|
+
# variable not declared in the statement but used in it.
|
372
|
+
# e.g. var_c = var_a+var_b var_a and var_b are the undeclared
|
373
|
+
# variable.
|
374
|
+
#
|
375
|
+
def not_declared_variables()
|
376
|
+
|
377
|
+
# The statement shouldn't be valid if there are undeclared variables
|
378
|
+
return [] if valid_syntax?
|
379
|
+
|
380
|
+
# Find any excluded variables(ones that are delcared within the statement)
|
381
|
+
# e.g. var = 'test'.chop
|
382
|
+
declared_variables = []
|
383
|
+
if( (self[0].kind_of? Variable)&&(self[1].class == Equal) )
|
384
|
+
declared_variables.push(self[0].copy)
|
385
|
+
end
|
386
|
+
|
387
|
+
# Attempt to find all the undeclared variables
|
388
|
+
undeclared = []
|
389
|
+
variables.each do |x|
|
390
|
+
next if declared_variables.any? {|y| y.uniq_id == x.uniq_id}
|
391
|
+
undeclared.push(x.copy)
|
392
|
+
end
|
393
|
+
return undeclared
|
394
|
+
|
395
|
+
end
|
396
|
+
|
397
|
+
# TODO It would be nicer to a subst that allows a block passed though
|
398
|
+
# e.g. a.subst(sub) {|x| x.variable_id == 9}
|
399
|
+
#
|
400
|
+
# Returns a new statement that is a duplicate of this statement
|
401
|
+
# but with the specified variable replaced.
|
402
|
+
#
|
403
|
+
# TODO Use the name nest for more than one statement
|
404
|
+
# TODO Write allot!! of tests for this
|
405
|
+
#
|
406
|
+
# @param id The id of the variable that will be replaced from the statement
|
407
|
+
#
|
408
|
+
def subst(id,var,context=self)
|
409
|
+
return self.copy.subst!(var){|x| x.uniq_id == id}
|
410
|
+
end
|
411
|
+
|
412
|
+
def subst!(var,&block)
|
413
|
+
|
414
|
+
# TODO I should probably check whether anything was substituted or or not
|
415
|
+
self.each_with_index do |x,i|
|
416
|
+
next unless x.kind_of?(Variable) or x.kind_of?(InstanceCallContainer)
|
417
|
+
|
418
|
+
# Essentially checks whether element is an InstanceCallContainer
|
419
|
+
if x.respond_to?(:subst!)
|
420
|
+
self[i] = x.subst!(var,&block)
|
421
|
+
next
|
422
|
+
end
|
423
|
+
|
424
|
+
if block.call(x)
|
425
|
+
self[i] = var.copy
|
426
|
+
end
|
427
|
+
end
|
428
|
+
return self
|
429
|
+
end
|
430
|
+
|
431
|
+
# TODO I should have a realised statement class
|
432
|
+
# Finds each element that satisfy the
|
433
|
+
#
|
434
|
+
# @param var The variable to replace any elements that match the block.
|
435
|
+
#
|
436
|
+
def replace_variable_if(var,&block)
|
437
|
+
container = self.copy.clear
|
438
|
+
self.each do |x|
|
439
|
+
if x.kind_of?(Variable)
|
440
|
+
if block.call(x)
|
441
|
+
container.push(var)
|
442
|
+
next
|
443
|
+
end
|
444
|
+
end
|
445
|
+
if x.kind_of?(InstanceCallContainer)
|
446
|
+
container.push(x.replace_variable_if(var,&block))
|
447
|
+
next
|
448
|
+
end
|
449
|
+
container.push(x.copy)
|
450
|
+
end
|
451
|
+
return container
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
# Returns a variable instance with the contextual requirements for the statement.
|
456
|
+
# TODO Don't really need these contextual_variable calls
|
457
|
+
#
|
458
|
+
def context_variable(id)
|
459
|
+
return find_variable(id)
|
460
|
+
end
|
461
|
+
|
462
|
+
def contextual_variable(id)
|
463
|
+
return context_variable(id)
|
464
|
+
end
|
465
|
+
|
466
|
+
# Returns the variable or instance call that contains
|
467
|
+
# the variable with the specidied id. It doesn't search
|
468
|
+
# the requirements.
|
469
|
+
#
|
470
|
+
# @param id The id of the variable that should be returned
|
471
|
+
#
|
472
|
+
# TODO Write test to retrieve declarared variable in a statement with two variables
|
473
|
+
# e.g. varA = varB-varC
|
474
|
+
#
|
475
|
+
# TODO Write tests for this
|
476
|
+
#
|
477
|
+
def find_variable(id)
|
478
|
+
|
479
|
+
# Go through each element of code in the statement and find instance calls and variables
|
480
|
+
self.each do |code|
|
481
|
+
next unless code.kind_of? Variable or code.kind_of? InstanceCallContainer
|
482
|
+
|
483
|
+
# Determine if the variable matches the id
|
484
|
+
if code.variable_id == id
|
485
|
+
|
486
|
+
# Does the statement declare a new variable?
|
487
|
+
if decalares_variable?
|
488
|
+
return find_variable_in_declaration_statement(code)
|
489
|
+
else
|
490
|
+
return code.copy
|
491
|
+
end
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
end
|
496
|
+
|
497
|
+
raise FailedToFindVariableError.new('Couldn\'t find variable with id = '+id.to_s+' in "'+self.write+'"')
|
498
|
+
end
|
499
|
+
|
500
|
+
# Returns the variable in this statement with uniq_id specified. In
|
501
|
+
# the event the variable resides in a instance call then that is
|
502
|
+
# returned.
|
503
|
+
#
|
504
|
+
def find_actual_variable(uniq_id)
|
505
|
+
target = self.variables.select {|x| x.uniq_id == uniq_id}
|
506
|
+
return target[0] unless target.empty?
|
507
|
+
raise FailedToFindVariableError.new('Couldn\'t find a variable with the id '+uniq_id.to_s+' in "'+self.write+'"')
|
508
|
+
end
|
509
|
+
|
510
|
+
# TODO This doesn't check anything
|
511
|
+
#
|
512
|
+
def replace_variable!(id,var)
|
513
|
+
|
514
|
+
unless var.kind_of?(Variable) then raise StandardError.new('Only expecting a variable') end
|
515
|
+
|
516
|
+
# Find the variable to be replaced
|
517
|
+
target = self.find_actual_variable(id)
|
518
|
+
|
519
|
+
#raise StandardError.new('Both target and variable should be the same class('+target.class.to_s+' != '+var.class.to_s+')') unless(target.kind_of?(var.class))
|
520
|
+
# TODO Should check for two variable kinds to two instance call kinds
|
521
|
+
self.each_with_index do |code,i|
|
522
|
+
|
523
|
+
# TODO Need to test with changing instance calls
|
524
|
+
next unless code.kind_of?(Variable) or code.kind_of?(InstanceCallContainer)
|
525
|
+
|
526
|
+
if(code.variable_id==target.variable_id)
|
527
|
+
|
528
|
+
if code.kind_of?(Variable)
|
529
|
+
self[i] = var
|
530
|
+
elsif code.kind_of?(InstanceCallContainer)
|
531
|
+
self[i].subject = var
|
532
|
+
end
|
533
|
+
return self
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
end
|
538
|
+
|
539
|
+
# Returns a declaration for this statement. So it will look something
|
540
|
+
# like -
|
541
|
+
# Statement.new(StringVariable.new('test'),Equal.new,'test')
|
542
|
+
#
|
543
|
+
# @param except An array of variable ids indicating what variables not to convert to
|
544
|
+
# new declarations. This is useful when you don't want to redclare an
|
545
|
+
# exisitng variable.
|
546
|
+
#
|
547
|
+
# e.g.
|
548
|
+
# Statement.new(Unknown.new,Equal.new,var_12,Addition.new,Literal.new(9))
|
549
|
+
#
|
550
|
+
def to_declaration(except=[])
|
551
|
+
if except.empty? then return VariableDeclaration.new('Statement',*self.collect {|x| x.to_declaration}) end
|
552
|
+
results = []
|
553
|
+
self.each do |x|
|
554
|
+
if x.kind_of?(Variable)
|
555
|
+
if except.any? {|y| x.variable_id == y}
|
556
|
+
results.push Declaration.new(x.write)
|
557
|
+
next
|
558
|
+
end
|
559
|
+
end
|
560
|
+
results.push x.to_declaration
|
561
|
+
end
|
562
|
+
return VariableDeclaration.new('Statement',*results)
|
563
|
+
end
|
564
|
+
|
565
|
+
# Returns the id of the variable declared in this statement
|
566
|
+
#
|
567
|
+
def declared_variable_id
|
568
|
+
raise UnexpectedStatementTypeError.new('Should be declaration statement type '+self.write) unless decalares_variable?
|
569
|
+
return self[0].variable_id
|
570
|
+
end
|
571
|
+
|
572
|
+
# Retuns an array of all the variable ids required for the
|
573
|
+
# statement.
|
574
|
+
#
|
575
|
+
def required_variable_ids
|
576
|
+
results = self.variables.collect {|x| x.variable_id}
|
577
|
+
return (results-[declared_variable_id])
|
578
|
+
end
|
579
|
+
|
580
|
+
# Returns true if the statement is simple. In that it just re-declares
|
581
|
+
# an existing variable e.g. var_a = var_b.
|
582
|
+
#
|
583
|
+
def is_simple?
|
584
|
+
return false unless decalares_variable?
|
585
|
+
# TODO Look at what you call the right hand side of the equation
|
586
|
+
if right_hand_side.length == 1 && right_hand_side[0].kind_of?(Variable)
|
587
|
+
return true
|
588
|
+
end
|
589
|
+
return false
|
590
|
+
end
|
591
|
+
|
592
|
+
# TODO Write tests
|
593
|
+
# This method writes the statement out as a literal string. In the sense
|
594
|
+
# that any of the variables used in the statement are converted to literals
|
595
|
+
# and written. Unknown variables are not written yet though.
|
596
|
+
#
|
597
|
+
# This method is called during tracking to give an indication what the statement
|
598
|
+
# being tracked is doing.
|
599
|
+
#
|
600
|
+
# TODO I am treating unknown variables as a special case that is the same
|
601
|
+
# value e.g. 'var' - but for determining equivalent processes it isn't
|
602
|
+
# ideal becuase you loose track of what variables is used in each
|
603
|
+
# statement. Although I'll wait unitl I can come up with an example
|
604
|
+
# and come up with a solution then.
|
605
|
+
#
|
606
|
+
def to_literal_string
|
607
|
+
return self.inject('') do |complete,part|
|
608
|
+
complete += part.to_literal_string
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
# Returns the number of statments in the statement. This will always
|
613
|
+
# be one but nested statements may have more.
|
614
|
+
#
|
615
|
+
def statement_count
|
616
|
+
return 1
|
617
|
+
end
|
618
|
+
|
619
|
+
# Returns true if the statement contains the same undeclared variables
|
620
|
+
# as the statement provided as well as the same number of elements.
|
621
|
+
#
|
622
|
+
# @param statement The statement thats undeclared variables are being compared.
|
623
|
+
#
|
624
|
+
def same_not_declared_variables?(statement)
|
625
|
+
return false if statement.length != self.length
|
626
|
+
statement.not_declared_variables.each do |x|
|
627
|
+
return false unless self.not_declared_variables.any? {|y| y.variable_id == x.variable_id}
|
628
|
+
end
|
629
|
+
return true
|
630
|
+
end
|
631
|
+
|
632
|
+
# Adds this statement to array of overriding statements if it
|
633
|
+
# overrides an existing variable.
|
634
|
+
#
|
635
|
+
# @param overriding_statements An array containing other overriding statements.
|
636
|
+
#
|
637
|
+
def find_overriding_statements(overriding_statements=[])
|
638
|
+
overriding_statements.push self if self.overrides?
|
639
|
+
return overriding_statements
|
640
|
+
end
|
641
|
+
|
642
|
+
# Returns an updated statement where all the variables within the statement
|
643
|
+
# have been realised. If the statement resides inside a nested statement
|
644
|
+
# or multiple nested statements the variables will be changed into a Dynamic
|
645
|
+
# Variable. These dynamic variables contain multiple variables with different
|
646
|
+
# values but the same variable_id.
|
647
|
+
#
|
648
|
+
# For example:
|
649
|
+
#
|
650
|
+
# 3.times do |var_a|
|
651
|
+
# var_b = var_a+1
|
652
|
+
# end
|
653
|
+
#
|
654
|
+
# In the above case var_b would be a dynamic variable would contain 3 FixnumVariable
|
655
|
+
# with the values 1,2,3. The statement itself doesn't indicate that it is nested, it
|
656
|
+
# is still just a regular statement.
|
657
|
+
#
|
658
|
+
# In a more complex example where we have two nested statements we might have something
|
659
|
+
# like the following:
|
660
|
+
#
|
661
|
+
# 2.times do |var_a|
|
662
|
+
# 2.times do |var_b|
|
663
|
+
# var_c = var_b+1
|
664
|
+
# end
|
665
|
+
# end
|
666
|
+
#
|
667
|
+
# We would end up with another dynamic variable with the values 1,2,1,2.
|
668
|
+
#
|
669
|
+
# The final tricky situation is when variables are overwritten. As a rule block
|
670
|
+
# variables are never overwritten - I should also include the subject of a loop
|
671
|
+
# as well.
|
672
|
+
#
|
673
|
+
# For example:
|
674
|
+
#
|
675
|
+
# var_a = 0
|
676
|
+
# 3.times do |var_b|
|
677
|
+
# var_a += var_b
|
678
|
+
# end
|
679
|
+
#
|
680
|
+
# In this situation there will be 3 versions of var_a the first one, the dynamic internal
|
681
|
+
# one and a post statement variable. The post statement variable will have a statement
|
682
|
+
# dependencey on the above nested statement. The internal variable will have a scope
|
683
|
+
# depenencey on the nested statement.
|
684
|
+
#
|
685
|
+
# TODO I suspect my approach to this is wrong. I pass in the history instance with
|
686
|
+
# all the information and then try to work out the realised value for this statement
|
687
|
+
# but it would probably be easier if the history instance itself to determine this.
|
688
|
+
#
|
689
|
+
def realise2(method_map)
|
690
|
+
return copy if realised?
|
691
|
+
|
692
|
+
raise StandardError.new('Unexpected data type ') unless method_map.kind_of?(History)
|
693
|
+
# Create a duplcate statement to be modified
|
694
|
+
result = self.copy
|
695
|
+
|
696
|
+
# Find value(s) for each unrealised variable.
|
697
|
+
y = []
|
698
|
+
each_unrealised_variable do |x|
|
699
|
+
if x.kind_of?(BlockVariable)
|
700
|
+
y.push(method_map.find_realised_variable(x.variable_id,x.uniq_id,'BlockVariable'))
|
701
|
+
else
|
702
|
+
y.push(method_map.find_realised_variable(x.variable_id,x.uniq_id))
|
703
|
+
end
|
704
|
+
# TODO Change to elsif x.kind_of?(Variable)
|
705
|
+
end
|
706
|
+
|
707
|
+
# Substitue the realised variables for the unrealised ones
|
708
|
+
self.each_unrealised_variable do |x|
|
709
|
+
catch(:variable_substituted) do
|
710
|
+
y.each do |z|
|
711
|
+
if z.uniq_id == x.uniq_id
|
712
|
+
result = result.replace_variable_if(z) {|a| a.uniq_id == x.uniq_id}
|
713
|
+
throw :variable_substituted
|
714
|
+
end
|
715
|
+
end
|
716
|
+
raise StandardError.new('Couldn\'t find realised value for variable with id '+x.variable_id.to_s+' in "'+self.write+'"')
|
717
|
+
end
|
718
|
+
end
|
719
|
+
return result
|
720
|
+
|
721
|
+
end
|
722
|
+
|
723
|
+
# Returns an array of all the RuntimeMethods instances contained in this
|
724
|
+
# statement. In all likelyhood there will no more than one.
|
725
|
+
#
|
726
|
+
def find_all_required_runtime_methods
|
727
|
+
defcalls = self.find_all {|x| x.kind_of?(DefCall)}
|
728
|
+
return defcalls.collect {|x| x.runtime_method}
|
729
|
+
end
|
730
|
+
|
731
|
+
# Returns true if the statement has been fully realsied - in that all the variables
|
732
|
+
# contained within it are realised. Otherwise it returns false.
|
733
|
+
#
|
734
|
+
def realised?
|
735
|
+
return self.variables.all? {|x| x.realised?}
|
736
|
+
end
|
737
|
+
|
738
|
+
# Returns the statement as a StatementVariable
|
739
|
+
#
|
740
|
+
# @param id The id of the variable to give to the returned statement
|
741
|
+
# variable.
|
742
|
+
#
|
743
|
+
def to_var(id=nil,uniq_id=nil)
|
744
|
+
var = StatementVariable.new(self.copy) {{:variable_id => id,:uniq_id=>uniq_id}}
|
745
|
+
return var
|
746
|
+
end
|
747
|
+
|
748
|
+
# Returns true if the statement declares a variable
|
749
|
+
def decalares_variable?
|
750
|
+
return true if self[0].kind_of?(Variable) and self[1].kind_of?(Equal)
|
751
|
+
return false
|
752
|
+
end
|
753
|
+
|
754
|
+
# Returns the variable declared in the statement. (if one exists)
|
755
|
+
#
|
756
|
+
def declared_variable
|
757
|
+
raise ImproperStatementUsageError.new(self.write+' does not declare a variable') unless decalares_variable?
|
758
|
+
return nil if declared_variable_id.nil?
|
759
|
+
return self[0]
|
760
|
+
end
|
761
|
+
|
762
|
+
# Returns true if this statement creates a new variable. A variable is considered created
|
763
|
+
# if it is declared in the variable or it modifies a variable. e.g. var_a.chop! or var_a.push(var_b)
|
764
|
+
#
|
765
|
+
def creates_variable?
|
766
|
+
created_variable
|
767
|
+
return true
|
768
|
+
rescue ImproperStatementUsageError
|
769
|
+
return false
|
770
|
+
end
|
771
|
+
|
772
|
+
# Returns the id of the created variable in this statement or raises an error if
|
773
|
+
# no variable is created.
|
774
|
+
#
|
775
|
+
def created_variable_id
|
776
|
+
return created_variable.variable_id
|
777
|
+
end
|
778
|
+
|
779
|
+
# Returns the variable created by this statement or raises an error if the
|
780
|
+
# statement doesn't create a variable.
|
781
|
+
#
|
782
|
+
def created_variable
|
783
|
+
|
784
|
+
# Check if the statement declares a new variable
|
785
|
+
begin
|
786
|
+
return declared_variable
|
787
|
+
rescue ImproperStatementUsageError => e
|
788
|
+
if first.kind_of?(InstanceCallContainer)
|
789
|
+
if first.subject.kind_of?(Variable) and first.method_call.destructive?
|
790
|
+
return first.subject
|
791
|
+
end
|
792
|
+
end
|
793
|
+
raise ImproperStatementUsageError.new(self.write+' statement does not create a new variable "'+e+"'")
|
794
|
+
end
|
795
|
+
|
796
|
+
end
|
797
|
+
|
798
|
+
# TODO Write tests for this
|
799
|
+
# Returns true if this statement is equivalent to the statement
|
800
|
+
# supplied. They are equalvalent if they have the same length,
|
801
|
+
# classes and values.
|
802
|
+
#
|
803
|
+
# @param statement The statement that this statement is being compared to
|
804
|
+
#
|
805
|
+
def equivalent?(statement)
|
806
|
+
return false unless statement.length == self.length
|
807
|
+
|
808
|
+
# TODO It would be nice to come up with a method to itterate through
|
809
|
+
# groups in an array.
|
810
|
+
# e.g. [self,statement].each_pair do |x,y|
|
811
|
+
# end
|
812
|
+
# where x is the first element in self and y is the first element in statement
|
813
|
+
# TODO I think Unknowns should only be accepted if they are the first paramter in
|
814
|
+
# a declaration statement. Current var_a = Unknown.chop is the same as var_b = var_c.chop
|
815
|
+
self.each_with_index do |elem,i|
|
816
|
+
return false unless elem.equivalent?(statement[i])
|
817
|
+
end
|
818
|
+
return true
|
819
|
+
|
820
|
+
# # TODO Tidy this up - pass the element to each other
|
821
|
+
# # e.g.
|
822
|
+
# # self.each_with_index do |elem,i|
|
823
|
+
# # return false unless eleme.equivalent?(statement[i])
|
824
|
+
# # end
|
825
|
+
# #
|
826
|
+
# self.each_with_index do |elem,i|
|
827
|
+
# return false if statement[i].class == elem.class
|
828
|
+
# if(elem.kind_of?(Variable))
|
829
|
+
# return false if elem.variable_id != statement[i].variable_id
|
830
|
+
# elsif(elem.kind_of?(InstanceCallContainer))
|
831
|
+
# return false if elem.variable_id != statement[i].variable_id
|
832
|
+
# return false if elem.method_call != statement[i].method_call
|
833
|
+
# end
|
834
|
+
# end
|
835
|
+
# return true
|
836
|
+
end
|
837
|
+
|
838
|
+
def has?(&block)
|
839
|
+
self.each do |x|
|
840
|
+
return true if block.call(x)
|
841
|
+
end
|
842
|
+
return false
|
843
|
+
end
|
844
|
+
|
845
|
+
# Returns the statement with the any variables in the conversions
|
846
|
+
# table replaced with the supplied variables. This is used when
|
847
|
+
# a runtime method with parameters needs evaluated.
|
848
|
+
#
|
849
|
+
# @param conversions A hash that identifies the uniq id's of the variables
|
850
|
+
# that should be replaced.
|
851
|
+
# e.g. {:6 => <#Variable>,:8 => <#Variable>} where variables with id 6 would
|
852
|
+
# changed to indicated variable.
|
853
|
+
#
|
854
|
+
def exchange_variables(conversions)
|
855
|
+
copied_statement = self.copy
|
856
|
+
conversions.each do |x,y|
|
857
|
+
begin
|
858
|
+
#copied_statement = copied_statement.subst(x.to_s.to_i,y.copy)
|
859
|
+
copied_statement = copied_statement.replace_variable_if(y) {|a| a.uniq_id == x.to_s.to_i}
|
860
|
+
rescue FailedToFindVariableError
|
861
|
+
next
|
862
|
+
end
|
863
|
+
end
|
864
|
+
return copied_statement
|
865
|
+
end
|
866
|
+
|
867
|
+
def cauldron_method_calls
|
868
|
+
return ['.statement_id']
|
869
|
+
end
|
870
|
+
|
871
|
+
def map_to(mapping)
|
872
|
+
|
873
|
+
# Duplicate the current statement before it is rewritten
|
874
|
+
rewritten_statement = self.copy
|
875
|
+
|
876
|
+
# Find all the containers that contain TheoryVariables
|
877
|
+
# NOTE The statement is put in an array because select all doesn't include the array itself
|
878
|
+
containers = [rewritten_statement].select_all {|x| x.respond_to?(:has?)}
|
879
|
+
theory_variable_containers = containers.select {|x| x.has? {|y| y.kind_of?(Variable)}}
|
880
|
+
|
881
|
+
# Rewrite the statement replacing the values
|
882
|
+
theory_variable_containers.each do |z|
|
883
|
+
z.replace_variables!(mapping)
|
884
|
+
end
|
885
|
+
|
886
|
+
return rewritten_statement
|
887
|
+
#return TheoryDependent.new(rewritten_statement,@theory_component_id)
|
888
|
+
end
|
889
|
+
|
890
|
+
protected
|
891
|
+
|
892
|
+
# Presumes this is a declaration statement and attempts to return
|
893
|
+
# specified variable in the context of the method.
|
894
|
+
#
|
895
|
+
def find_variable_in_declaration_statement(var)
|
896
|
+
|
897
|
+
# Duplicate the variable
|
898
|
+
# TODo I think I want to get rid of this "copy_contextual_variable"and just use copy
|
899
|
+
copied_variable = var.copy_contextual_variable
|
900
|
+
|
901
|
+
# Is the variable declared in this statement?
|
902
|
+
if(self[0].variable_id == copied_variable.variable_id)
|
903
|
+
|
904
|
+
# Create a requirement reflecting the declaration statement
|
905
|
+
# e.g varA = 'test'.chop becomes self = 'test'.chop
|
906
|
+
# statement_requirement = declared_variable_requirement
|
907
|
+
|
908
|
+
# Add this requirement to the variable and return it
|
909
|
+
# copied_variable.push(statement_requirement)
|
910
|
+
|
911
|
+
# TODO I think determining the value of variables at this point should
|
912
|
+
# be removed or re-thought. I think the typify method is more useful.
|
913
|
+
# The problem is that each statement is dependent on other statements
|
914
|
+
# or method calls.
|
915
|
+
|
916
|
+
# # NOTE I have included the untyped_variables call becuase it was continually throwing
|
917
|
+
# # since so many statements include method calls.
|
918
|
+
# if self.untyped_variables.length == 0 && !contains_method_call?
|
919
|
+
# # Convert the variable to be typed (if possible)
|
920
|
+
# if copied_variable.kind_of?(Unknown)
|
921
|
+
# begin
|
922
|
+
# return copied_variable.classify(CodeEvaluation.new.evaluate_code(literalise))
|
923
|
+
# # TODO I shouldn't use NameError - since I want to catch badly formed syntax - I
|
924
|
+
# # should have a custom error for bad syntax that will occu
|
925
|
+
# rescue NameError => e
|
926
|
+
# StandardLogger.log(' -- Statement: find_variable_in_declaration_statement'+e)
|
927
|
+
# end
|
928
|
+
# end
|
929
|
+
# end
|
930
|
+
|
931
|
+
return copied_variable
|
932
|
+
|
933
|
+
else
|
934
|
+
|
935
|
+
# The variable isn't declared in this statement
|
936
|
+
# So if varB is the declared variriable we might have
|
937
|
+
# varB = varA.chop
|
938
|
+
#
|
939
|
+
# This would create the requirement self.chop = varB
|
940
|
+
#
|
941
|
+
# Declare the statement requirement to be created
|
942
|
+
statement_requirement = nil
|
943
|
+
|
944
|
+
# Does the statement only have three elements
|
945
|
+
if(self.length == 3)
|
946
|
+
|
947
|
+
# Create a requirement for the variable in the statement
|
948
|
+
if(self[2].kind_of?(InstanceCallContainer))
|
949
|
+
|
950
|
+
# Create the matching requirement
|
951
|
+
statement_requirement = Requirement.new(InstanceCallContainer.new(This.new,self[2].method_call.class.new),Equal.new,self[0].copy)
|
952
|
+
|
953
|
+
elsif(self[2].kind_of?(Variable))
|
954
|
+
|
955
|
+
# Create the requirement reflecting the statement
|
956
|
+
statement_requirement = Requirement.new(This.new,Equal.new,self[0].copy)
|
957
|
+
|
958
|
+
else
|
959
|
+
raise StandardError.new('Unexpected class type '+self[2].class.to_s)
|
960
|
+
end
|
961
|
+
|
962
|
+
else
|
963
|
+
|
964
|
+
raise StandardError.new('Unexpected statement length'+self.length.to_s+' for requirement re-write') unless self.length == 5
|
965
|
+
|
966
|
+
# Handle the case were we have varA = varB-varC and we want varB's requirement
|
967
|
+
# e.g. we need varB = varA+varC
|
968
|
+
|
969
|
+
# TODO This can probably be re-written to be more abstract
|
970
|
+
# Case varA = varB-varC
|
971
|
+
if( self[2].kind_of?(Variable) and self[3].kind_of?(Subtract) and self[4].kind_of?(Variable) )
|
972
|
+
|
973
|
+
# where varB is the subject -> this = varA + varC
|
974
|
+
if(self[2].variable_id == var.variable_id)
|
975
|
+
statement_requirement = Requirement.new(This.new,Equal.new,self[0].copy,Addition.new,self[4].copy)
|
976
|
+
end
|
977
|
+
|
978
|
+
# where varC is the subject -> this = varB-varA
|
979
|
+
if(self[4].variable_id == var.variable_id)
|
980
|
+
statement_requirement = Requirement.new(This.new,Equal.new,self[2].copy,Subtract.new,self[0].copy)
|
981
|
+
end
|
982
|
+
|
983
|
+
elsif(self[2].kind_of?(Variable) and self[3].kind_of?(Addition) and self[4].kind_of?(Variable) )
|
984
|
+
|
985
|
+
if(self[2].variable_id == var.variable_id)
|
986
|
+
statement_requirement = Requirement.new(This.new,Equal.new,self[0].copy,Subtract.new,self[4].copy)
|
987
|
+
end
|
988
|
+
|
989
|
+
if(self[4].variable_id == var.variable_id)
|
990
|
+
statement_requirement = Requirement.new(This.new,Equal.new,self[2].copy,Addition.new,self[0].copy)
|
991
|
+
end
|
992
|
+
|
993
|
+
else
|
994
|
+
raise StandardError.new('Unknown statement structure "'+self.write+'"')
|
995
|
+
end
|
996
|
+
|
997
|
+
end
|
998
|
+
|
999
|
+
# Adding the new requirement to the variable
|
1000
|
+
copied_variable.push(statement_requirement)
|
1001
|
+
return copied_variable
|
1002
|
+
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# @param var The subsitute variable
|
1008
|
+
# @param id The id of the substitee
|
1009
|
+
# @param target The instance call being substituted
|
1010
|
+
#
|
1011
|
+
def subst_instance_call(var,id,target,context)
|
1012
|
+
|
1013
|
+
# Duplicate the current statement (variables are not in context)
|
1014
|
+
copied_statement = self.copy
|
1015
|
+
|
1016
|
+
# 2. Outside the context of the statement does the variable meet the requirements
|
1017
|
+
#begin
|
1018
|
+
|
1019
|
+
# If the target is a instance call does the new variable meet the requirements of the instance call?
|
1020
|
+
var_with_instance_call = nil
|
1021
|
+
|
1022
|
+
# Can the replacing variable be litralised?
|
1023
|
+
if(var.literalisable?)
|
1024
|
+
|
1025
|
+
# Change the variable into a literal instance
|
1026
|
+
begin
|
1027
|
+
literal_instance = var.literalise
|
1028
|
+
rescue StandardError => e
|
1029
|
+
StandardLogger.log('literalise: '+e)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# Retrieve the instance call requirements and use the litralised variable with them
|
1033
|
+
unless target.method_call.valid_syntax?(literal_instance)
|
1034
|
+
raise RuntimeSyntaxError.new(literal_instance.write+' doesn\'t have the method "'+target.method_call.write+'"')
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# The substitute variable can use the same instance call as the variable being substituted
|
1038
|
+
var_with_instance_call = InstanceCallContainer.new(var,target.method_call.copy)
|
1039
|
+
|
1040
|
+
else
|
1041
|
+
|
1042
|
+
# TODO Write tests to test variables that can't be literalised
|
1043
|
+
# self.class == String can use the .chop method call
|
1044
|
+
|
1045
|
+
# Does the substitute variable meet the requirements of the target variable
|
1046
|
+
if(var.meets_requirements?(target.subject))
|
1047
|
+
|
1048
|
+
# The individual variables can be replaced - but can it use the same method call as the target
|
1049
|
+
var_with_instance_call = InstanceCallContainer.new(var,target.method_call.copy)
|
1050
|
+
|
1051
|
+
else
|
1052
|
+
StandardLogger.log(var.write_variable_id+' doesn\'t meet the requirements of '+target.subject.write)
|
1053
|
+
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
# At this point the variable being substitued either doesn't make an instance call or makes a valid one
|
1058
|
+
# Now we need to see whether it is valid with in the context of the statement.
|
1059
|
+
|
1060
|
+
# Get the requirements added to the variable in the context of the statement
|
1061
|
+
reqs = requirements_for(target.subject)
|
1062
|
+
|
1063
|
+
# If there are no requirements then its fine to use
|
1064
|
+
if(reqs.empty?)
|
1065
|
+
copied_statement = self.copy
|
1066
|
+
#copied_statement.replace_variable!(id,var_with_instance_call)
|
1067
|
+
copied_statement.replace_variable!(id,var_with_instance_call.subject)
|
1068
|
+
return copied_statement
|
1069
|
+
end
|
1070
|
+
StandardLogger.log('Statement: haven\'t accounted for requirements')
|
1071
|
+
return
|
1072
|
+
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
#
|
1076
|
+
# @param var The variable that is being substituted into the statement
|
1077
|
+
# @param id The id of the variable to be included in the statement
|
1078
|
+
# @param target The variable in the statement that will be replaced
|
1079
|
+
#
|
1080
|
+
def subst_variable_call(var,id,target,context)
|
1081
|
+
|
1082
|
+
# The variable must meet the requirements of the statement - so add it to a duplicate statement
|
1083
|
+
copied_statement = self.copy
|
1084
|
+
copied_statement.replace_variable!(id,var)
|
1085
|
+
|
1086
|
+
return copied_statement
|
1087
|
+
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
# Returns a copy of the variable with the id supplied, otherwise
|
1091
|
+
# raise an exception. This variable is not in the context of
|
1092
|
+
# the statement.
|
1093
|
+
#
|
1094
|
+
# @param id The id of the variable that should exist in this statement
|
1095
|
+
#
|
1096
|
+
def copy_original_variable(id)
|
1097
|
+
self.each do |code|
|
1098
|
+
next unless code.kind_of? Variable or code.kind_of? InstanceCallContainer
|
1099
|
+
return code.copy if code.variable_id == id
|
1100
|
+
end
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
# Updates any of the requirements of the variables if
|
1104
|
+
# it's needed and confirms the statement so it can be
|
1105
|
+
# used in a method.
|
1106
|
+
# TODO This class might be redundant
|
1107
|
+
#
|
1108
|
+
# Write tests for exceptable statements
|
1109
|
+
#
|
1110
|
+
def update
|
1111
|
+
|
1112
|
+
# Narrow down what type of statement it is. Does it contain a equal or equivalent sign?
|
1113
|
+
equivaltent_assignment = detect_class(Equivalent)
|
1114
|
+
|
1115
|
+
unless equivaltent_assignment.nil?
|
1116
|
+
begin
|
1117
|
+
#update_requirements
|
1118
|
+
@confirmed = true
|
1119
|
+
rescue InvalidStatementError
|
1120
|
+
end
|
1121
|
+
return
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
# Check if the statement contains return syntax
|
1125
|
+
if detect_class(Return)
|
1126
|
+
|
1127
|
+
# Check there are two
|
1128
|
+
unless self.length == 2
|
1129
|
+
@confirmed = false
|
1130
|
+
return
|
1131
|
+
end
|
1132
|
+
@confirmed = true
|
1133
|
+
return
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
if self.length == 1 and detect_class(DefCall)
|
1137
|
+
@confirmed = true
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
# This runs some simple checks of the structure of the statement
|
1143
|
+
# for example if the statement had two = signs then that would
|
1144
|
+
# invalidate it.
|
1145
|
+
#
|
1146
|
+
def validate_statment_structure
|
1147
|
+
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
# Returns the two arrays split between the kind of class
|
1151
|
+
# provided.
|
1152
|
+
#
|
1153
|
+
# @param klass
|
1154
|
+
#
|
1155
|
+
def split klass
|
1156
|
+
|
1157
|
+
# Find the index of the equivalent assignment
|
1158
|
+
equivaltent_assignment = detect_class(klass)
|
1159
|
+
if equivaltent_assignment.nil? then raise InvalidStatementError.new('No equivalent assignment found when spliting') end
|
1160
|
+
equalivalent_index = self.index(equivaltent_assignment)
|
1161
|
+
|
1162
|
+
# Slice the array between the two values
|
1163
|
+
left = self.slice(0...equalivalent_index)
|
1164
|
+
right = self.slice((equalivalent_index+1)...self.length)
|
1165
|
+
|
1166
|
+
# Check there is only ever one thing on the left hand side of the equation
|
1167
|
+
if(left.length > 1) then throw InvalidStatementError.new('"Equivalent statements" can only have one value on the left hand side') end
|
1168
|
+
|
1169
|
+
return [left, right]
|
1170
|
+
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
# Returns the first instance with the class declared.
|
1174
|
+
#
|
1175
|
+
# @param class_name The kind of class that is being searched for e.g. Equal or Equivalent
|
1176
|
+
#
|
1177
|
+
def detect_class(class_name)
|
1178
|
+
not_found = lambda{raise StandardError.new(''+class_name.to_s+' could not be found')}
|
1179
|
+
begin
|
1180
|
+
return self.detect(not_found) {|x| x.kind_of? class_name}
|
1181
|
+
rescue StandardError
|
1182
|
+
return nil
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
# Returns the requirement for the declared variable in the statement
|
1187
|
+
#
|
1188
|
+
def declared_variable_requirement
|
1189
|
+
|
1190
|
+
# Check that this statement declares a variable (they all should)
|
1191
|
+
if(detect_class(Equal).nil?)then raise StandardError.new('Expecting statement to declare a variable') end
|
1192
|
+
|
1193
|
+
# Check that the equals sign is the second code
|
1194
|
+
unless(self[1].kind_of?(Equal)) then raise StandardError.new('Expecting statement to include equal sign as secound code element') end
|
1195
|
+
|
1196
|
+
# Return the requirement of the declared variable
|
1197
|
+
declaration_requirement = Requirement.new(This.new,Equal.new,*self[2..self.length])
|
1198
|
+
|
1199
|
+
return declaration_requirement
|
1200
|
+
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
# Currently this involves changing the 'uniq_id' of any
|
1204
|
+
# variables modified in the statment.
|
1205
|
+
# e.g. var_a.push(9)
|
1206
|
+
#
|
1207
|
+
def review
|
1208
|
+
|
1209
|
+
# TODO I have dropped this for now I don't know if it is redundent or not - it effect
|
1210
|
+
# tc_statement.rb test_created_variable
|
1211
|
+
# # A modifying statement would be "var_16.push('Normandy SR2')"
|
1212
|
+
# if statement_type == StatementStructure::MODIFYING_STATEMENT
|
1213
|
+
# created_variable.uniq_id_history.push(created_variable.uniq_id)
|
1214
|
+
# created_variable.increament_uniq_id!
|
1215
|
+
# end
|
1216
|
+
rescue StandardError => e
|
1217
|
+
# TODO I shouldn't really need this it is just temporary because of the way
|
1218
|
+
# structures are created in StatementStructure2.statements - they create a
|
1219
|
+
# partial statement.
|
1220
|
+
StandardLogger.instance.warning(e)
|
1221
|
+
end
|
1222
|
+
|
1223
|
+
end
|