gobstones 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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