gobstones 0.0.1.1

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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.simplecov +3 -0
  6. data/.travis.yml +4 -0
  7. data/CHANGELOG +4 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +47 -0
  10. data/LICENSE +675 -0
  11. data/README.md +17 -0
  12. data/Rakefile +4 -0
  13. data/bin/gobstones +12 -0
  14. data/gobstones.gemspec +24 -0
  15. data/lib/gobstones/cli/board_template +39 -0
  16. data/lib/gobstones/cli/printer.rb +106 -0
  17. data/lib/gobstones/cli/runner.rb +52 -0
  18. data/lib/gobstones/extensions/all.rb +2 -0
  19. data/lib/gobstones/extensions/boolean.rb +17 -0
  20. data/lib/gobstones/extensions/fixnum.rb +9 -0
  21. data/lib/gobstones/lang/all.rb +5 -0
  22. data/lib/gobstones/lang/commands/all.rb +13 -0
  23. data/lib/gobstones/lang/commands/assignments.rb +29 -0
  24. data/lib/gobstones/lang/commands/boom_cmd.rb +28 -0
  25. data/lib/gobstones/lang/commands/command_block.rb +37 -0
  26. data/lib/gobstones/lang/commands/conditional_cmd.rb +27 -0
  27. data/lib/gobstones/lang/commands/if_cmd.rb +38 -0
  28. data/lib/gobstones/lang/commands/ir_al_origen_cmd.rb +19 -0
  29. data/lib/gobstones/lang/commands/mover_cmd.rb +25 -0
  30. data/lib/gobstones/lang/commands/poner_cmd.rb +31 -0
  31. data/lib/gobstones/lang/commands/procedure_call.rb +23 -0
  32. data/lib/gobstones/lang/commands/repeat_with_cmd.rb +73 -0
  33. data/lib/gobstones/lang/commands/sacar_cmd.rb +31 -0
  34. data/lib/gobstones/lang/commands/skip_cmd.rb +19 -0
  35. data/lib/gobstones/lang/commands/vaciar_tablero_cmd.rb +19 -0
  36. data/lib/gobstones/lang/commands/while_cmd.rb +25 -0
  37. data/lib/gobstones/lang/definitions/all.rb +4 -0
  38. data/lib/gobstones/lang/definitions/definition.rb +32 -0
  39. data/lib/gobstones/lang/definitions/definition_call.rb +24 -0
  40. data/lib/gobstones/lang/definitions/function.rb +14 -0
  41. data/lib/gobstones/lang/definitions/main.rb +26 -0
  42. data/lib/gobstones/lang/definitions/no_return_statement.rb +15 -0
  43. data/lib/gobstones/lang/definitions/procedure.rb +44 -0
  44. data/lib/gobstones/lang/definitions/return_from_function.rb +22 -0
  45. data/lib/gobstones/lang/definitions/return_from_main.rb +22 -0
  46. data/lib/gobstones/lang/definitions/var_tuple.rb +26 -0
  47. data/lib/gobstones/lang/expressions/all.rb +8 -0
  48. data/lib/gobstones/lang/expressions/arithmetic_expressions.rb +52 -0
  49. data/lib/gobstones/lang/expressions/boolean_expressions.rb +29 -0
  50. data/lib/gobstones/lang/expressions/comparison_expressions.rb +45 -0
  51. data/lib/gobstones/lang/expressions/function_call.rb +15 -0
  52. data/lib/gobstones/lang/expressions/one_arg_expression.rb +21 -0
  53. data/lib/gobstones/lang/expressions/parentheses_expression.rb +13 -0
  54. data/lib/gobstones/lang/expressions/primitive_functions.rb +68 -0
  55. data/lib/gobstones/lang/expressions/two_arg_expression.rb +34 -0
  56. data/lib/gobstones/lang/expressions/type_bound_functions.rb +66 -0
  57. data/lib/gobstones/lang/expressions/var_name.rb +31 -0
  58. data/lib/gobstones/lang/literals/all.rb +4 -0
  59. data/lib/gobstones/lang/literals/booleans.rb +109 -0
  60. data/lib/gobstones/lang/literals/colors.rb +94 -0
  61. data/lib/gobstones/lang/literals/directions.rb +137 -0
  62. data/lib/gobstones/lang/literals/literal.rb +63 -0
  63. data/lib/gobstones/lang/literals/number.rb +49 -0
  64. data/lib/gobstones/lang/program.rb +32 -0
  65. data/lib/gobstones/modules/equal_by_class.rb +13 -0
  66. data/lib/gobstones/parser/ast/ast.rb +210 -0
  67. data/lib/gobstones/parser/grammar/gobstones.treetop +316 -0
  68. data/lib/gobstones/parser/parse_error.rb +17 -0
  69. data/lib/gobstones/parser/treetop_parser.rb +73 -0
  70. data/lib/gobstones/runner/all.rb +6 -0
  71. data/lib/gobstones/runner/board.rb +57 -0
  72. data/lib/gobstones/runner/cell.rb +60 -0
  73. data/lib/gobstones/runner/errors/all.rb +8 -0
  74. data/lib/gobstones/runner/errors/boom_error.rb +11 -0
  75. data/lib/gobstones/runner/errors/definition_not_found_error.rb +19 -0
  76. data/lib/gobstones/runner/errors/empty_cell_error.rb +11 -0
  77. data/lib/gobstones/runner/errors/gobstones_runtime_error.rb +11 -0
  78. data/lib/gobstones/runner/errors/gobstones_type_error.rb +11 -0
  79. data/lib/gobstones/runner/errors/out_of_board_error.rb +11 -0
  80. data/lib/gobstones/runner/errors/undefined_variable_error.rb +11 -0
  81. data/lib/gobstones/runner/errors/wrong_arguments_error.rb +11 -0
  82. data/lib/gobstones/runner/execution_context.rb +89 -0
  83. data/lib/gobstones/runner/head.rb +101 -0
  84. data/lib/gobstones/type_check_result.rb +16 -0
  85. data/spec/lang/commands/assignments_spec.rb +13 -0
  86. data/spec/lang/commands/boom_cmd_spec.rb +8 -0
  87. data/spec/lang/commands/cmd_block_spec.rb +21 -0
  88. data/spec/lang/commands/if_cmd_spec.rb +49 -0
  89. data/spec/lang/commands/ir_al_origen_cmd_spec.rb +16 -0
  90. data/spec/lang/commands/mover_cmd_spec.rb +38 -0
  91. data/spec/lang/commands/poner_cmd_spec.rb +29 -0
  92. data/spec/lang/commands/procedure_call_spec.rb +44 -0
  93. data/spec/lang/commands/procedure_spec.rb +67 -0
  94. data/spec/lang/commands/repeat_with_cmd_spec.rb +41 -0
  95. data/spec/lang/commands/sacar_cmd_spec.rb +34 -0
  96. data/spec/lang/commands/skip_cmd_spec.rb +12 -0
  97. data/spec/lang/commands/vaciar_tablero_cmd_spec.rb +13 -0
  98. data/spec/lang/commands/while_cmd_spec.rb +37 -0
  99. data/spec/lang/expressions/arithmetic_expressions_spec.rb +108 -0
  100. data/spec/lang/expressions/boolean_expressions_spec.rb +56 -0
  101. data/spec/lang/expressions/comparison_expressions_spec.rb +285 -0
  102. data/spec/lang/expressions/primitive_functions_spec.rb +126 -0
  103. data/spec/lang/expressions/type_bound_functions_spec.rb +39 -0
  104. data/spec/lang/expressions/var_name_spec.rb +16 -0
  105. data/spec/lang/literals/booleans_spec.rb +13 -0
  106. data/spec/lang/literals/colors_spec.rb +13 -0
  107. data/spec/lang/literals/directions_spec.rb +45 -0
  108. data/spec/lang/literals/numbers_spec.rb +8 -0
  109. data/spec/matchers/parse_matcher.rb +84 -0
  110. data/spec/parser/arithmetic_expressions_spec.rb +129 -0
  111. data/spec/parser/assignments_spec.rb +39 -0
  112. data/spec/parser/boolean_expressions_spec.rb +111 -0
  113. data/spec/parser/command_block_spec.rb +50 -0
  114. data/spec/parser/data_types_spec.rb +67 -0
  115. data/spec/parser/function_calls_spec.rb +37 -0
  116. data/spec/parser/function_definitions_spec.rb +50 -0
  117. data/spec/parser/gobstones_program_spec.rb +55 -0
  118. data/spec/parser/if_command_spec.rb +46 -0
  119. data/spec/parser/main_definition_spec.rb +42 -0
  120. data/spec/parser/nested_expressions_spec.rb +39 -0
  121. data/spec/parser/primitive_expressions_spec.rb +105 -0
  122. data/spec/parser/procedure_calls_spec.rb +36 -0
  123. data/spec/parser/procedure_definitions_spec.rb +39 -0
  124. data/spec/parser/repeat_with_command_spec.rb +23 -0
  125. data/spec/parser/simple_commands_spec.rb +62 -0
  126. data/spec/parser/treetop_parser_spec.rb +109 -0
  127. data/spec/parser/var_tuple_spec.rb +30 -0
  128. data/spec/parser/while_command_spec.rb +30 -0
  129. data/spec/runner/board_spec.rb +85 -0
  130. data/spec/runner/cell_spec.rb +72 -0
  131. data/spec/runner/execution_context_spec.rb +64 -0
  132. data/spec/runner/head_spec.rb +139 -0
  133. data/spec/spec_helper.rb +15 -0
  134. data/spec/type_checker_spec.rb +37 -0
  135. metadata +242 -0
@@ -0,0 +1,16 @@
1
+ module Gobstones
2
+
3
+ class TypeCheckResult
4
+
5
+ def initialize(expected, actual)
6
+ @expected = expected
7
+ @actual = actual
8
+ end
9
+
10
+ def ok?
11
+ @expected == @actual
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,13 @@
1
+ describe SingleAssignment do
2
+
3
+ let(:context) { ExecutionContext.new }
4
+ let(:a) { VarName.new 'a' }
5
+
6
+ it "should associate the var name with the evaluated expression" do
7
+ assign = SingleAssignment.new a, Add.new(3.to_gbs_num, 4.to_gbs_num)
8
+ assign.evaluate context
9
+ expect(context.has_variable_named?('a')).to be_true
10
+ expect(context.get(a)).to eq(7.to_gbs_num)
11
+ end
12
+
13
+ end
@@ -0,0 +1,8 @@
1
+ describe Boom do
2
+
3
+ it "should raise an exception when evaluating, with the right message" do
4
+ msg = 'This is a program error'
5
+ expect { Boom.new(msg).evaluate }.to raise_error(BoomError, msg)
6
+ end
7
+
8
+ end
@@ -0,0 +1,21 @@
1
+ describe CmdBlock do
2
+
3
+ it "should evaluate all inner commands" do
4
+ program = Program.new [], nil
5
+ context = ProgramExecutionContext.for program
6
+ cmd_block = CmdBlock.new [
7
+ Poner.new(Rojo.new), Poner.new(Verde.new),
8
+ Poner.new(Negro.new), Poner.new(Azul.new)]
9
+ cmd_block.evaluate context
10
+ expect(context.head.are_there_balls?(Rojo.new)).to be_true
11
+ expect(context.head.are_there_balls?(Azul.new)).to be_true
12
+ expect(context.head.are_there_balls?(Negro.new)).to be_true
13
+ expect(context.head.are_there_balls?(Verde.new)).to be_true
14
+ end
15
+
16
+ it "builds an empty command block" do
17
+ expect(CmdBlock.new []).to be_empty
18
+ expect(CmdBlock.empty).to be_empty
19
+ end
20
+
21
+ end
@@ -0,0 +1,49 @@
1
+ describe "if command" do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:then_block) { CmdBlock.new [Poner.new(Verde.new)] }
5
+ let(:else_block) { CmdBlock.new [Poner.new(Rojo.new)] }
6
+
7
+ describe "if-then" do
8
+
9
+ it "should evaluate the block if the condition is true" do
10
+ if_cmd = IfCmd.new True.new, then_block
11
+ if_cmd.evaluate context
12
+ expect(context.head.are_there_balls?(Verde.new)).to be_true
13
+ end
14
+
15
+ it "should not evaluate the block if the condition is false" do
16
+ if_cmd = IfCmd.new False.new, then_block
17
+ if_cmd.evaluate context
18
+ expect(context.head.are_there_balls?(Verde.new)).to be_false
19
+ end
20
+
21
+ it "should raise a type error if the condition is not boolean" do
22
+ [Number.new(42), Norte.new, Verde.new].each do |value|
23
+ if_cmd = IfCmd.new value, then_block
24
+ expect { if_cmd.evaluate context }
25
+ .to raise_error(GobstonesTypeError, /is not a boolean/)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ describe "if-then-else" do
32
+
33
+ it "should evaluate the 'then' block and not evaluate the 'else' block" do
34
+ if_cmd = IfElseCmd.new True.new, then_block, else_block
35
+ if_cmd.evaluate context
36
+ expect(context.head.are_there_balls?(Verde.new)).to be_true
37
+ expect(context.head.are_there_balls?(Rojo.new)).to be_false
38
+ end
39
+
40
+ it "should not evaluate the 'then' block and evaluate the 'else' block" do
41
+ if_cmd = IfElseCmd.new False.new, then_block, else_block
42
+ if_cmd.evaluate context
43
+ expect(context.head.are_there_balls?(Verde.new)).to be_false
44
+ expect(context.head.are_there_balls?(Rojo.new)).to be_true
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,16 @@
1
+ describe IrAlOrigen do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+
5
+ it "should evaluate and put the head in 0, 0" do
6
+ context.head.move_north
7
+ context.head.move_east
8
+ ir_al_origen = IrAlOrigen.new
9
+
10
+ ir_al_origen.evaluate context
11
+
12
+ expect(context.head.x_pos).to eq(0)
13
+ expect(context.head.y_pos).to eq(0)
14
+ end
15
+
16
+ end
@@ -0,0 +1,38 @@
1
+ describe Mover do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:north) { Norte.new }
5
+ let(:south) { Sur.new }
6
+
7
+ it "should run" do
8
+ Mover.new(north).evaluate(context)
9
+
10
+ expect(context.head.x_pos).to eq(0)
11
+ expect(context.head.y_pos).to eq(1)
12
+ end
13
+
14
+ it "should undo" do
15
+ cmd = Mover.new(north)
16
+
17
+ cmd.evaluate context
18
+ cmd.undo context
19
+
20
+ expect(context.head.x_pos).to eq(0)
21
+ expect(context.head.y_pos).to eq(0)
22
+ end
23
+
24
+ it "should return opposite cmd" do
25
+ expect(Mover.new(north).opposite).to eq(Mover.new(south))
26
+ end
27
+
28
+ it "should fail if types don't match" do
29
+ expect { Mover.new(Verde.new).evaluate(context) }.
30
+ to raise_error(GobstonesTypeError, /is not a direction/)
31
+ end
32
+
33
+ it "should fail when out of board" do
34
+ expect { Mover.new(south).evaluate(context) }.
35
+ to raise_error(OutOfBoardError)
36
+ end
37
+
38
+ end
@@ -0,0 +1,29 @@
1
+ describe Poner do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:green) { Verde.new }
5
+
6
+ it "should execute" do
7
+ Poner.new(green).evaluate(context)
8
+
9
+ expect(context.head.number_of_balls(green)).to eq(1)
10
+ end
11
+
12
+ it "should undo" do
13
+ context.head.put green
14
+
15
+ Poner.new(green).undo context
16
+
17
+ expect(context.head.number_of_balls(green)).to eq(0)
18
+ end
19
+
20
+ it "should return the opposite cmd" do
21
+ expect(Poner.new(green).opposite).to eq(Sacar.new(green))
22
+ end
23
+
24
+ it "should fail if types don't match" do
25
+ expect { Poner.new(Norte.new).evaluate(context) }.
26
+ to raise_error(GobstonesTypeError, /is not a color/)
27
+ end
28
+
29
+ end
@@ -0,0 +1,44 @@
1
+ describe "procedure calls" do
2
+
3
+ let(:empty_args) { VarTuple.new [] }
4
+ let(:empty_body) { CmdBlock.empty }
5
+
6
+ it "should execute an existing procedure when calling it" do
7
+ poner_cmd = Poner.new Verde.new
8
+ body = CmdBlock.new [poner_cmd]
9
+ my_procedure = Procedure.new 'MyProcedure', empty_args, body
10
+ program = Program.new [my_procedure], nil
11
+ context = ProgramExecutionContext.for program
12
+
13
+ proc_call = ProcedureCall.new 'MyProcedure', []
14
+ proc_call.evaluate context
15
+
16
+ expect(context.head.are_there_balls?(Verde.new)).to be_true
17
+ end
18
+
19
+ it "should allow to call a procedure from another procedure" do
20
+ poner_cmd = Poner.new Azul.new
21
+ inner_procedure_body = CmdBlock.new [poner_cmd]
22
+ inner_procedure = Procedure.new 'Inner', empty_args, inner_procedure_body
23
+ call_to_inner_procedure = ProcedureCall.new 'Inner', []
24
+ outer_procedure_body = CmdBlock.new [call_to_inner_procedure]
25
+ outer_procedure = Procedure.new 'Outer', empty_args, outer_procedure_body
26
+ program = Program.new [outer_procedure, inner_procedure], nil
27
+ program_context = ProgramExecutionContext.for program
28
+
29
+ call_to_outer_procedure = ProcedureCall.new 'Outer', []
30
+ call_to_outer_procedure.evaluate program_context
31
+
32
+ expect(program_context.head.are_there_balls?(Azul.new)).to be_true
33
+ end
34
+
35
+ it "should fail to execute an undefined procedure" do
36
+ program = Program.new [], nil
37
+ context = ProgramExecutionContext.for program
38
+ proc_call = ProcedureCall.new 'UndefinedProcedure', []
39
+
40
+ expect { proc_call.evaluate context }
41
+ .to raise_error(DefinitionNotFound, DefinitionNotFound.message_for('UndefinedProcedure'))
42
+ end
43
+
44
+ end
@@ -0,0 +1,67 @@
1
+ describe Procedure do
2
+
3
+ let(:program_context) { ProgramExecutionContext.for Program.new([], nil) }
4
+ let(:empty_args) { VarTuple.new [] }
5
+ let(:empty_body) { CmdBlock.empty }
6
+
7
+ it "should execute its body and leave state in the program context" do
8
+ poner_cmd = Poner.new Rojo.new
9
+ body = CmdBlock.new [poner_cmd]
10
+ procedure = Procedure.new 'MyProcedure', empty_args, body
11
+ procedure.evaluate program_context
12
+ expect(program_context.head.are_there_balls?(Rojo.new)).to be_true
13
+ end
14
+
15
+ it "should execute in a new clean context, without having variables defined in another contexts" do
16
+ var_name = VarName.new 'var'
17
+ program_context.set var_name, Verde.new
18
+
19
+ poner_cmd = Poner.new var_name
20
+ body = CmdBlock.new [poner_cmd]
21
+ procedure = Procedure.new 'MyProcedure', empty_args, body
22
+ expect { procedure.evaluate program_context }
23
+ .to raise_error(UndefinedVariableError)
24
+ end
25
+
26
+ it "should set arguments in the new context and they can be used" do
27
+ a_color = VarName.new 'a_color'
28
+ a_direction = VarName.new 'a_direction'
29
+ args = VarTuple.new [a_color, a_direction]
30
+ mover_cmd = Mover.new a_direction
31
+ poner_cmd = Poner.new a_color
32
+ body = CmdBlock.new [mover_cmd, poner_cmd]
33
+ procedure = Procedure.new 'MyProc', args, body
34
+
35
+ procedure.evaluate program_context, [Negro.new, Norte.new]
36
+
37
+ expect(program_context.head.are_there_balls?(Negro.new)).to be_true
38
+ expect(program_context.head.y_pos).to eq(1)
39
+ end
40
+
41
+ it "should not set arguments as var names in outer context" do
42
+ a_direction = VarName.new 'a_direction'
43
+ args = VarTuple.new [a_direction]
44
+ procedure = Procedure.new 'MyProc', args, empty_body
45
+
46
+ procedure.evaluate program_context, [Oeste.new]
47
+
48
+ expect(program_context.has_variable_named?('a_direction')).to be_false
49
+ end
50
+
51
+ it "should fail if it is executed with more arguments than expected" do
52
+ procedure = Procedure.new 'MyProcedure', empty_args, empty_body
53
+ error_message = "Wrong number of arguments in procedure 'MyProcedure': expected 0, got 1"
54
+ expect { procedure.evaluate program_context, [Norte.new] }
55
+ .to raise_error(WrongArgumentsError, error_message)
56
+ end
57
+
58
+ it "should fail if it is executed with less arguments than expected" do
59
+ arg1, arg2 = VarName.new('arg1'), VarName.new('arg2')
60
+ args = VarTuple.new [arg1, arg2]
61
+ procedure = Procedure.new 'MyProcedure2', args, empty_body
62
+ error_message = "Wrong number of arguments in procedure 'MyProcedure2': expected 2, got 1"
63
+ expect { procedure.evaluate program_context, [Verde.new] }
64
+ .to raise_error(WrongArgumentsError, error_message)
65
+ end
66
+
67
+ end
@@ -0,0 +1,41 @@
1
+ describe RepeatWithCmd do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:var_name) { VarName.new 'var' }
5
+
6
+ it "should iterate over numbers" do
7
+ repeat_with = RepeatWithCmd.new var_name, 1.to_gbs_num, 10.to_gbs_num, CmdBlock.new([Poner.new(Rojo.new)])
8
+ repeat_with.evaluate context
9
+ expect(context.head.number_of_balls(Rojo.new)).to eq(10)
10
+ end
11
+
12
+ it "should throw an error if the range values have not the same type" do
13
+ repeat_with = RepeatWithCmd.new var_name, 1.to_gbs_num, Este.new, CmdBlock.new([])
14
+ expect { repeat_with.evaluate context }
15
+ .to raise_error(GobstonesTypeError, /types don't match in range values/)
16
+ end
17
+
18
+ it "should throw an error if the index variable is previously defined" do
19
+ repeat_with = RepeatWithCmd.new var_name, 1.to_gbs_num, 5.to_gbs_num, CmdBlock.new([])
20
+ context.set var_name, 42.to_gbs_num
21
+ expect { repeat_with.evaluate context }
22
+ .to raise_error(GobstonesRuntimeError, /index variable can't be used because it's already defined/)
23
+ end
24
+
25
+ it "should remove the index variable assignment after execution" do
26
+ repeat_with = RepeatWithCmd.new var_name, Azul.new, Verde.new, CmdBlock.new([])
27
+ repeat_with.evaluate context
28
+ expect(context.has_variable_named?('var')).to be_false
29
+ end
30
+
31
+ it "should allow to use the index variable inside the command block" do
32
+ cmd_block = CmdBlock.new [Poner.new(VarName.new('var'))]
33
+ repeat_with = RepeatWithCmd.new var_name, Azul.new, Verde.new, cmd_block
34
+ repeat_with.evaluate context
35
+ expect(context.head.are_there_balls?(Azul.new)).to be_true
36
+ expect(context.head.are_there_balls?(Negro.new)).to be_true
37
+ expect(context.head.are_there_balls?(Rojo.new)).to be_true
38
+ expect(context.head.are_there_balls?(Verde.new)).to be_true
39
+ end
40
+
41
+ end
@@ -0,0 +1,34 @@
1
+ describe Sacar do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:red) { Rojo.new }
5
+
6
+ it "should execute" do
7
+ 3.times { context.head.put red }
8
+
9
+ Sacar.new(red).evaluate context
10
+
11
+ expect(context.head.number_of_balls(red)).to eq(2)
12
+ end
13
+
14
+ it "should undo" do
15
+ Sacar.new(red).undo context
16
+
17
+ expect(context.head.number_of_balls(red)).to eq(1)
18
+ end
19
+
20
+ it "should return the opposite cmd" do
21
+ expect(Sacar.new(red).opposite).to eq(Poner.new(red))
22
+ end
23
+
24
+ it "should fail if there are no balls in the board" do
25
+ expect { Sacar.new(red).evaluate(context) }.
26
+ to raise_error(EmptyCellError)
27
+ end
28
+
29
+ it "should fail if types don't match" do
30
+ expect { Sacar.new(True.new).evaluate(context) }.
31
+ to raise_error(GobstonesTypeError, /is not a color/)
32
+ end
33
+
34
+ end
@@ -0,0 +1,12 @@
1
+ describe Skip do
2
+
3
+ let(:context) { ProgramExecutionContext.for Program.new([], nil) }
4
+
5
+ it "evaluates and does nothing" do
6
+ Skip.new.evaluate context
7
+ expect(context.head.x_pos).to eq(0)
8
+ expect(context.head.y_pos).to eq(0)
9
+ expect(context.head.board).to be_empty
10
+ end
11
+
12
+ end
@@ -0,0 +1,13 @@
1
+ describe VaciarTablero do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:red) { Rojo.new }
5
+
6
+ it "should empty the board in the context" do
7
+ context.head.put red
8
+ vaciar_tablero = VaciarTablero.new
9
+ vaciar_tablero.evaluate context
10
+ expect(context.board.empty?).to be_true
11
+ end
12
+
13
+ end
@@ -0,0 +1,37 @@
1
+ describe WhileCmd do
2
+
3
+ let(:context) { ProgramExecutionContext.for double('GobstonesProgram') }
4
+ let(:while_block) { CmdBlock.new [Poner.new(Verde.new)] }
5
+
6
+ def condition(times)
7
+ cond = double('while condition')
8
+ expected_values = [True.new] * times + [False.new]
9
+ allow(cond).to receive(:evaluate).and_return(*expected_values)
10
+ cond
11
+ end
12
+
13
+ it "should evaluate the command block until the condition is not satisfied" do
14
+ while_cmd = WhileCmd.new condition(3), while_block
15
+ while_cmd.evaluate context
16
+ expect(context.head.number_of_balls(Verde.new)).to eq(3)
17
+ end
18
+
19
+ it "should not evaluate the command block if the condition is not satisfied" do
20
+ while_cmd = WhileCmd.new condition(0), while_block
21
+ while_cmd.evaluate context
22
+ expect(context.head.are_there_balls?(Verde.new)).to be_false
23
+ end
24
+
25
+ it "should raise a type error if the condition is not boolean" do
26
+ while_cmd = WhileCmd.new Sur.new, while_block
27
+ expect { while_cmd.evaluate context }
28
+ .to raise_error(GobstonesTypeError, /is not a boolean/)
29
+ end
30
+
31
+ it "should fail by stack overflow if the condition is always true" do
32
+ while_cmd = WhileCmd.new True.new, while_block
33
+ expect { while_cmd.evaluate context }
34
+ .to raise_error(GobstonesRuntimeError, /stack overflow/)
35
+ end
36
+
37
+ end