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.
Files changed (398) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/LICENSE.txt +20 -0
  4. data/README +1 -0
  5. data/README.rdoc +19 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/bin/cauldron +10 -0
  9. data/cauldron/.autotest +23 -0
  10. data/cauldron/History.txt +6 -0
  11. data/cauldron/Manifest.txt +8 -0
  12. data/cauldron/README.txt +57 -0
  13. data/cauldron/Rakefile +27 -0
  14. data/cauldron/bin/cauldron +3 -0
  15. data/cauldron/lib/cauldron.rb +3 -0
  16. data/cauldron/test/test_cauldron.rb +8 -0
  17. data/features/cauldron_example_cases.feature +13 -0
  18. data/features/cauldron_generates_runtime_method.feature +12 -0
  19. data/features/cauldron_start_terminal.feature +13 -0
  20. data/features/step_definitions/cauldron_steps.rb +16 -0
  21. data/features/step_definitions/terminal_steps.rb +34 -0
  22. data/features/support/env.rb +19 -0
  23. data/lib/Chain.rb +879 -0
  24. data/lib/ChainMapping.rb +172 -0
  25. data/lib/CodeHandler.rb +517 -0
  26. data/lib/Mapping.rb +26 -0
  27. data/lib/MappingValues.rb +24 -0
  28. data/lib/ScopeDependencies.rb +7 -0
  29. data/lib/Theory.rb +274 -0
  30. data/lib/UnifiedChain.rb +110 -0
  31. data/lib/cauldron.rb +6 -0
  32. data/lib/cauldron/conversion.rb +15 -0
  33. data/lib/cauldron/pot.rb +134 -0
  34. data/lib/cauldron/sexp2cauldron.rb +156 -0
  35. data/lib/cauldron/terminal.rb +120 -0
  36. data/lib/cauldron/theory_factory.rb +10 -0
  37. data/lib/core/ActsAsCode.rb +25 -0
  38. data/lib/core/BlockToken.rb +33 -0
  39. data/lib/core/CCall.rb +7 -0
  40. data/lib/core/CTestCase.rb +27 -0
  41. data/lib/core/ClassMethodCallContainer.rb +45 -0
  42. data/lib/core/Container.rb +85 -0
  43. data/lib/core/InstanceCallContainer.rb +272 -0
  44. data/lib/core/MethodUsage.rb +66 -0
  45. data/lib/core/PrintVariables.rb +25 -0
  46. data/lib/core/TheoryGenerator.rb +764 -0
  47. data/lib/core/Token.rb +7 -0
  48. data/lib/core/assignment/Assignment.rb +18 -0
  49. data/lib/core/assignment/Equal.rb +39 -0
  50. data/lib/core/assignment/Equivalent.rb +20 -0
  51. data/lib/core/assignment/NotEqual.rb +14 -0
  52. data/lib/core/call_container/CallContainer.rb +66 -0
  53. data/lib/core/class_method_call/New.rb +15 -0
  54. data/lib/core/class_method_call/RuntimeClassMethodCall.rb +31 -0
  55. data/lib/core/declaration/Declaration.rb +16 -0
  56. data/lib/core/declaration/LiteralDeclaration.rb +47 -0
  57. data/lib/core/declaration/VariableDeclaration.rb +59 -0
  58. data/lib/core/instance_call/ArrayEach.rb +23 -0
  59. data/lib/core/instance_call/ArrayLength.rb +15 -0
  60. data/lib/core/instance_call/Chop.rb +28 -0
  61. data/lib/core/instance_call/Copy.rb +22 -0
  62. data/lib/core/instance_call/DeclaredVariable.rb +18 -0
  63. data/lib/core/instance_call/InstanceCall.rb +77 -0
  64. data/lib/core/instance_call/Params.rb +15 -0
  65. data/lib/core/instance_call/Push.rb +20 -0
  66. data/lib/core/instance_call/StringLength.rb +32 -0
  67. data/lib/core/instance_call/Times.rb +20 -0
  68. data/lib/core/instance_call/instance_calls.rb +168 -0
  69. data/lib/core/instance_call/length_equal.rb +15 -0
  70. data/lib/core/kernal/EvalCall.rb +15 -0
  71. data/lib/core/kernal/LocalVariablesCall.rb +15 -0
  72. data/lib/core/literal/Literal.rb +111 -0
  73. data/lib/core/literal/Raw.rb +23 -0
  74. data/lib/core/literal/RuntimeMethodLiteral.rb +21 -0
  75. data/lib/core/literal/StatementLiteral.rb +28 -0
  76. data/lib/core/method_call/AvailableVariablesCall.rb +25 -0
  77. data/lib/core/method_call/ClassCall.rb +33 -0
  78. data/lib/core/method_call/DefCall.rb +72 -0
  79. data/lib/core/method_call/EvaluateClassCall.rb +29 -0
  80. data/lib/core/method_call/MethodNameCall.rb +27 -0
  81. data/lib/core/method_call/ToDeclarationCall.rb +15 -0
  82. data/lib/core/requirement/Requirement.rb +291 -0
  83. data/lib/core/runtime_class/ArrayClass.rb +19 -0
  84. data/lib/core/runtime_class/ClassCallClass.rb +23 -0
  85. data/lib/core/runtime_class/ClassEvaluationClass.rb +19 -0
  86. data/lib/core/runtime_class/ClassName.rb +18 -0
  87. data/lib/core/runtime_class/DefCallClass.rb +21 -0
  88. data/lib/core/runtime_class/EqualClass.rb +19 -0
  89. data/lib/core/runtime_class/FixnumClass.rb +15 -0
  90. data/lib/core/runtime_class/IfStatementClass.rb +12 -0
  91. data/lib/core/runtime_class/InstanceCallClass.rb +19 -0
  92. data/lib/core/runtime_class/InstanceCallContainerClass.rb +16 -0
  93. data/lib/core/runtime_class/InstanceClassCallClass.rb +19 -0
  94. data/lib/core/runtime_class/LiteralClass.rb +19 -0
  95. data/lib/core/runtime_class/MethodParameterClass.rb +27 -0
  96. data/lib/core/runtime_class/MethodUsageClass.rb +27 -0
  97. data/lib/core/runtime_class/RequirementClass.rb +19 -0
  98. data/lib/core/runtime_class/ReturnClass.rb +21 -0
  99. data/lib/core/runtime_class/RuntimeClass.rb +30 -0
  100. data/lib/core/runtime_class/RuntimeClassClass.rb +19 -0
  101. data/lib/core/runtime_class/RuntimeMethodClass.rb +34 -0
  102. data/lib/core/runtime_class/StatementClass.rb +53 -0
  103. data/lib/core/runtime_class/StringClass.rb +23 -0
  104. data/lib/core/runtime_class/StringLengthClass.rb +19 -0
  105. data/lib/core/runtime_class/StringVariableClass.rb +19 -0
  106. data/lib/core/runtime_class/ThisClass.rb +15 -0
  107. data/lib/core/runtime_class/UnknownClass.rb +23 -0
  108. data/lib/core/runtime_class/class_names.rb +95 -0
  109. data/lib/core/runtime_class/runtime_class.rb +123 -0
  110. data/lib/core/runtime_method/ActsAsRuntimeMethod.rb +300 -0
  111. data/lib/core/runtime_method/ParametersContainer.rb +29 -0
  112. data/lib/core/runtime_method/RealisedRuntimeMethod.rb +94 -0
  113. data/lib/core/runtime_method/RuntimeMethod.rb +817 -0
  114. data/lib/core/runtime_method/WriteParameters.rb +35 -0
  115. data/lib/core/statement/ActsAsStatement.rb +116 -0
  116. data/lib/core/statement/ArrayAccess.rb +96 -0
  117. data/lib/core/statement/BlockStatement.rb +348 -0
  118. data/lib/core/statement/DeclarationStatement.rb +19 -0
  119. data/lib/core/statement/HackStatement.rb +25 -0
  120. data/lib/core/statement/HashAccess.rb +18 -0
  121. data/lib/core/statement/OpenStatement.rb +171 -0
  122. data/lib/core/statement/RealisedStatement.rb +5 -0
  123. data/lib/core/statement/SingleLineBlockStatement.rb +39 -0
  124. data/lib/core/statement/Statement.rb +1223 -0
  125. data/lib/core/statement/StatementDependencies.rb +271 -0
  126. data/lib/core/statement/StatementGroup.rb +157 -0
  127. data/lib/core/statement/StatementStructure2.rb +224 -0
  128. data/lib/core/statement/TheoryStatement.rb +60 -0
  129. data/lib/core/statement/TopologicalStatements.rb +34 -0
  130. data/lib/core/structure/DeclareNewInstanceStructure.rb +49 -0
  131. data/lib/core/structure/DeclareRuntimeMethodStructure.rb +34 -0
  132. data/lib/core/structure/DeclareVariableAsLiteralStructure.rb +31 -0
  133. data/lib/core/structure/DeclareVariableAsVariableStructure.rb +52 -0
  134. data/lib/core/structure/FixnumAdditionStructure.rb +56 -0
  135. data/lib/core/structure/InstanceCallContainerStructure.rb +50 -0
  136. data/lib/core/structure/InstanceCallStructure.rb +53 -0
  137. data/lib/core/structure/InstanceMethodCallStructure.rb +21 -0
  138. data/lib/core/structure/ReturnStructure.rb +20 -0
  139. data/lib/core/structure/StatementStructure.rb +11 -0
  140. data/lib/core/syntax/Addition.rb +25 -0
  141. data/lib/core/syntax/BlockContainer.rb +130 -0
  142. data/lib/core/syntax/Boolean.rb +15 -0
  143. data/lib/core/syntax/Code.rb +11 -0
  144. data/lib/core/syntax/Do.rb +20 -0
  145. data/lib/core/syntax/False.rb +12 -0
  146. data/lib/core/syntax/If.rb +28 -0
  147. data/lib/core/syntax/IfContainer.rb +100 -0
  148. data/lib/core/syntax/Nil.rb +15 -0
  149. data/lib/core/syntax/Return.rb +33 -0
  150. data/lib/core/syntax/Subtract.rb +19 -0
  151. data/lib/core/syntax/This.rb +40 -0
  152. data/lib/core/syntax/True.rb +20 -0
  153. data/lib/core/syntax/syntax.rb +24 -0
  154. data/lib/core/tracking/ActsAsTrackable.rb +65 -0
  155. data/lib/core/tracking/History.rb +167 -0
  156. data/lib/core/tracking/RuntimeTrackingMethod.rb +32 -0
  157. data/lib/core/tracking/Step.rb +52 -0
  158. data/lib/core/variable/ArrayVariable.rb +76 -0
  159. data/lib/core/variable/BaseVariable.rb +154 -0
  160. data/lib/core/variable/BlockVariable.rb +92 -0
  161. data/lib/core/variable/FixnumVariable.rb +36 -0
  162. data/lib/core/variable/HistoryVariable.rb +8 -0
  163. data/lib/core/variable/MethodParameter.rb +206 -0
  164. data/lib/core/variable/MethodUsageVariable.rb +60 -0
  165. data/lib/core/variable/NilVariable.rb +29 -0
  166. data/lib/core/variable/RuntimeMethodParameter.rb +67 -0
  167. data/lib/core/variable/StatementVariable.rb +72 -0
  168. data/lib/core/variable/StepVariable.rb +7 -0
  169. data/lib/core/variable/StringVariable.rb +46 -0
  170. data/lib/core/variable/TypeVariable.rb +72 -0
  171. data/lib/core/variable/Unknown.rb +116 -0
  172. data/lib/core/variable/UnknownVariable.rb +29 -0
  173. data/lib/core/variable/Variable.rb +70 -0
  174. data/lib/core/variable/VariableContainer.rb +28 -0
  175. data/lib/core/variable/VariableIncluded.rb +27 -0
  176. data/lib/core/variable/VariableReference.rb +85 -0
  177. data/lib/error/FailedToFindStatementContainerError.rb +7 -0
  178. data/lib/error/FailedToFindStatementError.rb +7 -0
  179. data/lib/error/FailedToFindVariableError.rb +7 -0
  180. data/lib/error/FailedToLiteraliseError.rb +7 -0
  181. data/lib/error/FailedVariableMatch.rb +7 -0
  182. data/lib/error/ImproperStatementUsageError.rb +7 -0
  183. data/lib/error/IncompatiableRequirementsError.rb +7 -0
  184. data/lib/error/InvalidStatementError.rb +7 -0
  185. data/lib/error/MethodSizeError.rb +7 -0
  186. data/lib/error/RuntimeSyntaxError.rb +7 -0
  187. data/lib/error/UnexpectedStatementTypeError.rb +7 -0
  188. data/lib/error/UnknownStatementType.rb +7 -0
  189. data/lib/error/UnliteralisableError.rb +7 -0
  190. data/lib/implemented_chain.rb +34 -0
  191. data/lib/intrinsic/IntrinsicLastRuntimeMethod.rb +20 -0
  192. data/lib/intrinsic/IntrinsicLiteral.rb +26 -0
  193. data/lib/intrinsic/IntrinsicObject.rb +22 -0
  194. data/lib/intrinsic/IntrinsicRuntimeMethod.rb +27 -0
  195. data/lib/intrinsic/IntrinsicTestCases.rb +17 -0
  196. data/lib/logger/StandardLogger.rb +62 -0
  197. data/lib/required.rb +236 -0
  198. data/lib/ruby_code/Array.rb +95 -0
  199. data/lib/ruby_code/Fixnum.rb +39 -0
  200. data/lib/ruby_code/Hash.rb +25 -0
  201. data/lib/ruby_code/NilClass.rb +19 -0
  202. data/lib/ruby_code/Object.rb +24 -0
  203. data/lib/ruby_code/String.rb +86 -0
  204. data/lib/ruby_code/Symbol.rb +7 -0
  205. data/lib/standard_library/Tasks.rb +12 -0
  206. data/lib/theories.rb +143 -0
  207. data/lib/theory/ActionImplementation.rb +17 -0
  208. data/lib/theory/TheoryAction.rb +70 -0
  209. data/lib/theory/TheoryChainValidator.rb +101 -0
  210. data/lib/theory/TheoryComponent.rb +42 -0
  211. data/lib/theory/TheoryConnector.rb +755 -0
  212. data/lib/theory/TheoryDependent.rb +135 -0
  213. data/lib/theory/TheoryImplementation.rb +74 -0
  214. data/lib/theory/TheoryResult.rb +131 -0
  215. data/lib/theory/TheoryVariable.rb +63 -0
  216. data/lib/theory/theory_collection.rb +62 -0
  217. data/lib/util/ClassEvaluation.rb +68 -0
  218. data/lib/util/CodeEvaluation.rb +35 -0
  219. data/lib/util/DeclarationStatementEvaluation.rb +31 -0
  220. data/lib/util/MethodEvaluation.rb +49 -0
  221. data/lib/util/MethodTester.rb +71 -0
  222. data/lib/util/MethodValidation.rb +145 -0
  223. data/lib/util/MethodWriter.rb +66 -0
  224. data/lib/util/Parser.rb +299 -0
  225. data/lib/util/StatementCheck.rb +42 -0
  226. data/lib/util/StringToTheory.rb +119 -0
  227. data/lib/util/System.rb +8 -0
  228. data/spec/cauldron/pot_spec.rb +6 -0
  229. data/spec/cauldron/runtime_method_spec.rb +36 -0
  230. data/spec/cauldron/sexp_2_cauldron_spec.rb +26 -0
  231. data/spec/cauldron/terminal_spec.rb +38 -0
  232. data/spec/cauldron/theory_action_spec.rb +5 -0
  233. data/spec/spec_helper.rb +4 -0
  234. data/test/fixtures/chains/1/declaration.txt +26 -0
  235. data/test/fixtures/chains/1/dump +0 -0
  236. data/test/fixtures/chains/2/declaration.txt +26 -0
  237. data/test/fixtures/chains/2/dump +0 -0
  238. data/test/fixtures/chains/3/declaration.txt +26 -0
  239. data/test/fixtures/chains/3/dump +0 -0
  240. data/test/fixtures/implementation_results/0/declaration.txt +3 -0
  241. data/test/fixtures/implementation_results/0/dump +0 -0
  242. data/test/fixtures/theories/0/declaration.txt +9 -0
  243. data/test/fixtures/theories/0/desc +10 -0
  244. data/test/fixtures/theories/0/dump +0 -0
  245. data/test/fixtures/theories/1/declaration.txt +15 -0
  246. data/test/fixtures/theories/1/desc +11 -0
  247. data/test/fixtures/theories/1/dump +0 -0
  248. data/test/fixtures/theories/10/declaration.txt +23 -0
  249. data/test/fixtures/theories/10/desc +17 -0
  250. data/test/fixtures/theories/10/dump +0 -0
  251. data/test/fixtures/theories/11/declaration.txt +20 -0
  252. data/test/fixtures/theories/11/desc +14 -0
  253. data/test/fixtures/theories/11/dump +0 -0
  254. data/test/fixtures/theories/12/declaration.txt +18 -0
  255. data/test/fixtures/theories/12/desc +12 -0
  256. data/test/fixtures/theories/12/dump +0 -0
  257. data/test/fixtures/theories/13/declaration.txt +24 -0
  258. data/test/fixtures/theories/13/desc +20 -0
  259. data/test/fixtures/theories/13/dump +0 -0
  260. data/test/fixtures/theories/14/declaration.txt +26 -0
  261. data/test/fixtures/theories/14/desc +20 -0
  262. data/test/fixtures/theories/14/dump +0 -0
  263. data/test/fixtures/theories/15/declaration.txt +20 -0
  264. data/test/fixtures/theories/15/desc +14 -0
  265. data/test/fixtures/theories/15/dump +0 -0
  266. data/test/fixtures/theories/16/declaration.txt +30 -0
  267. data/test/fixtures/theories/16/desc +14 -0
  268. data/test/fixtures/theories/16/dump +0 -0
  269. data/test/fixtures/theories/17/declaration.txt +25 -0
  270. data/test/fixtures/theories/17/desc +11 -0
  271. data/test/fixtures/theories/17/dump +0 -0
  272. data/test/fixtures/theories/18/declaration.txt +23 -0
  273. data/test/fixtures/theories/18/desc +11 -0
  274. data/test/fixtures/theories/18/dump +0 -0
  275. data/test/fixtures/theories/19/declaration.txt +23 -0
  276. data/test/fixtures/theories/19/desc +11 -0
  277. data/test/fixtures/theories/19/dump +0 -0
  278. data/test/fixtures/theories/2/declaration.txt +12 -0
  279. data/test/fixtures/theories/2/desc +10 -0
  280. data/test/fixtures/theories/2/dump +0 -0
  281. data/test/fixtures/theories/20/declaration.txt +23 -0
  282. data/test/fixtures/theories/20/desc +17 -0
  283. data/test/fixtures/theories/20/dump +0 -0
  284. data/test/fixtures/theories/3/declaration.txt +19 -0
  285. data/test/fixtures/theories/3/desc +11 -0
  286. data/test/fixtures/theories/3/dump +0 -0
  287. data/test/fixtures/theories/4/declaration.txt +11 -0
  288. data/test/fixtures/theories/4/desc +11 -0
  289. data/test/fixtures/theories/4/dump +0 -0
  290. data/test/fixtures/theories/5/declaration.txt +6 -0
  291. data/test/fixtures/theories/5/desc +9 -0
  292. data/test/fixtures/theories/5/dump +0 -0
  293. data/test/fixtures/theories/6/declaration.txt +13 -0
  294. data/test/fixtures/theories/6/desc +11 -0
  295. data/test/fixtures/theories/6/dump +0 -0
  296. data/test/fixtures/theories/7/declaration.txt +19 -0
  297. data/test/fixtures/theories/7/desc +11 -0
  298. data/test/fixtures/theories/7/dump +0 -0
  299. data/test/fixtures/theories/8/declaration.txt +21 -0
  300. data/test/fixtures/theories/8/desc +11 -0
  301. data/test/fixtures/theories/8/dump +0 -0
  302. data/test/fixtures/theories/9/declaration.txt +24 -0
  303. data/test/fixtures/theories/9/desc +20 -0
  304. data/test/fixtures/theories/9/dump +0 -0
  305. data/test/fixtures/theory_implementations/0/declaration.txt +11 -0
  306. data/test/fixtures/theory_implementations/0/desc.txt +9 -0
  307. data/test/fixtures/theory_implementations/0/dump +0 -0
  308. data/test/fixtures/theory_implementations/0/theory_id +1 -0
  309. data/test/fixtures/theory_implementations/1/desc.txt +9 -0
  310. data/test/fixtures/theory_implementations/1/dump +0 -0
  311. data/test/fixtures/theory_implementations/1/theory_id +1 -0
  312. data/test/fixtures/theory_implementations/2/desc.txt +9 -0
  313. data/test/fixtures/theory_implementations/2/dump +0 -0
  314. data/test/fixtures/theory_implementations/2/theory_id +1 -0
  315. data/test/output/simple_method.txt +6 -0
  316. data/test/output/test_method/first_possible_method.txt +6 -0
  317. data/test/output/test_simple_cases/simple_case_01.txt +8 -0
  318. data/test/output/test_simple_cases/simple_case_02.txt +7 -0
  319. data/test/output/test_simple_cases/simple_case_03.txt +8 -0
  320. data/test/output/test_simple_cases/simple_case_04.txt +8 -0
  321. data/test/output/test_simple_cases/simple_case_05.txt +8 -0
  322. data/test/output/test_simple_cases/simple_case_06.txt +9 -0
  323. data/test/output/test_simple_cases/simple_case_07.txt +9 -0
  324. data/test/output/test_simple_cases/simple_case_08.txt +9 -0
  325. data/test/tc_contextual_variables.rb +87 -0
  326. data/test/tc_describe.rb +47 -0
  327. data/test/tc_method.rb +133 -0
  328. data/test/tc_requirement.rb +30 -0
  329. data/test/tc_suite_complete.rb +26 -0
  330. data/test/tc_variable.rb +52 -0
  331. data/test/ts_complete.rb +84 -0
  332. data/test/ts_stable.rb +81 -0
  333. data/test/unit/core/declaration/tc_literal_declaration.rb +34 -0
  334. data/test/unit/core/method_call/tc_class_call.rb +20 -0
  335. data/test/unit/core/runtime_method/tc_realised_runtime_method.rb +129 -0
  336. data/test/unit/core/runtime_method/tc_runtime_method.rb +616 -0
  337. data/test/unit/core/statement/tc_array_access.rb +63 -0
  338. data/test/unit/core/statement/tc_block_statement.rb +51 -0
  339. data/test/unit/core/statement/tc_hack_statement.rb +26 -0
  340. data/test/unit/core/statement/tc_open_statement.rb +70 -0
  341. data/test/unit/core/statement/tc_statement.rb +681 -0
  342. data/test/unit/core/statement/tc_statement_dependencies.rb +146 -0
  343. data/test/unit/core/statement/tc_statement_group.rb +35 -0
  344. data/test/unit/core/statement/tc_statement_replace_variable.rb +61 -0
  345. data/test/unit/core/statement/tc_theory_statement.rb +51 -0
  346. data/test/unit/core/structure/tc_declare_new_instance_structure.rb +41 -0
  347. data/test/unit/core/structure/tc_declare_variable_as_literal_structure.rb +41 -0
  348. data/test/unit/core/structure/tc_declare_variable_as_variable_structure.rb +66 -0
  349. data/test/unit/core/structure/tc_instance_call_container_structure.rb +41 -0
  350. data/test/unit/core/structure/tc_return_structure.rb +32 -0
  351. data/test/unit/core/syntax/tc_block_container.rb +32 -0
  352. data/test/unit/core/syntax/tc_if_container.rb +39 -0
  353. data/test/unit/core/tc_class_method_call.rb +34 -0
  354. data/test/unit/core/tc_container.rb +41 -0
  355. data/test/unit/core/tc_ctest_case.rb +25 -0
  356. data/test/unit/core/tc_instance_call_container.rb +93 -0
  357. data/test/unit/core/tc_literal.rb +30 -0
  358. data/test/unit/core/tc_theory_generator.rb +336 -0
  359. data/test/unit/core/tc_theory_generator_heavy.rb +42 -0
  360. data/test/unit/core/tracking/tc_history.rb +102 -0
  361. data/test/unit/core/tracking/tc_step.rb +65 -0
  362. data/test/unit/core/variable/tc_array_variable.rb +61 -0
  363. data/test/unit/core/variable/tc_block_variable.rb +17 -0
  364. data/test/unit/core/variable/tc_fixnum_variable.rb +54 -0
  365. data/test/unit/core/variable/tc_method_parameter_variable.rb +22 -0
  366. data/test/unit/core/variable/tc_runtime_method_variable.rb +32 -0
  367. data/test/unit/core/variable/tc_string_variable.rb +37 -0
  368. data/test/unit/core/variable/tc_unknown.rb +24 -0
  369. data/test/unit/core/variable/tc_variable_reference.rb +28 -0
  370. data/test/unit/ruby_code/tc_array.rb +64 -0
  371. data/test/unit/ruby_code/tc_fixnum.rb +18 -0
  372. data/test/unit/ruby_code/tc_hash.rb +41 -0
  373. data/test/unit/ruby_code/tc_string.rb +38 -0
  374. data/test/unit/tc_chain.rb +434 -0
  375. data/test/unit/tc_chain_mapping.rb +62 -0
  376. data/test/unit/tc_chain_with_case_1.rb +169 -0
  377. data/test/unit/tc_instance_call.rb +40 -0
  378. data/test/unit/tc_instance_call_structure.rb +35 -0
  379. data/test/unit/tc_method_usage.rb +35 -0
  380. data/test/unit/tc_pot.rb +124 -0
  381. data/test/unit/tc_runtime_tracking_method.rb +40 -0
  382. data/test/unit/tc_statement_structure_2.rb +43 -0
  383. data/test/unit/tc_theory.rb +533 -0
  384. data/test/unit/tc_variable_declaration.rb +32 -0
  385. data/test/unit/theory/tc_theory_action.rb +80 -0
  386. data/test/unit/theory/tc_theory_action_implementation.rb +23 -0
  387. data/test/unit/theory/tc_theory_chain_validator.rb +340 -0
  388. data/test/unit/theory/tc_theory_connector.rb +396 -0
  389. data/test/unit/theory/tc_theory_dependent.rb +151 -0
  390. data/test/unit/theory/tc_theory_implementation.rb +133 -0
  391. data/test/unit/theory/tc_theory_result.rb +111 -0
  392. data/test/unit/theory/tc_theory_variable.rb +45 -0
  393. data/test/unit/util/tc_method_validation.rb +98 -0
  394. data/test/unit/util/tc_parser.rb +108 -0
  395. data/test/unit/util/tc_string_to_theory.rb +299 -0
  396. data/test/unit/variable/tc_method_usage_variable.rb +25 -0
  397. data/tmp/runtime_method_evaluation.rb +10 -0
  398. 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,5 @@
1
+ class RealisedStatement < Array
2
+ include PrintVariables
3
+ include ActsAsStatement
4
+
5
+ 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