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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.simplecov +3 -0
- data/.travis.yml +4 -0
- data/CHANGELOG +4 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +675 -0
- data/README.md +17 -0
- data/Rakefile +4 -0
- data/bin/gobstones +12 -0
- data/gobstones.gemspec +24 -0
- data/lib/gobstones/cli/board_template +39 -0
- data/lib/gobstones/cli/printer.rb +106 -0
- data/lib/gobstones/cli/runner.rb +52 -0
- data/lib/gobstones/extensions/all.rb +2 -0
- data/lib/gobstones/extensions/boolean.rb +17 -0
- data/lib/gobstones/extensions/fixnum.rb +9 -0
- data/lib/gobstones/lang/all.rb +5 -0
- data/lib/gobstones/lang/commands/all.rb +13 -0
- data/lib/gobstones/lang/commands/assignments.rb +29 -0
- data/lib/gobstones/lang/commands/boom_cmd.rb +28 -0
- data/lib/gobstones/lang/commands/command_block.rb +37 -0
- data/lib/gobstones/lang/commands/conditional_cmd.rb +27 -0
- data/lib/gobstones/lang/commands/if_cmd.rb +38 -0
- data/lib/gobstones/lang/commands/ir_al_origen_cmd.rb +19 -0
- data/lib/gobstones/lang/commands/mover_cmd.rb +25 -0
- data/lib/gobstones/lang/commands/poner_cmd.rb +31 -0
- data/lib/gobstones/lang/commands/procedure_call.rb +23 -0
- data/lib/gobstones/lang/commands/repeat_with_cmd.rb +73 -0
- data/lib/gobstones/lang/commands/sacar_cmd.rb +31 -0
- data/lib/gobstones/lang/commands/skip_cmd.rb +19 -0
- data/lib/gobstones/lang/commands/vaciar_tablero_cmd.rb +19 -0
- data/lib/gobstones/lang/commands/while_cmd.rb +25 -0
- data/lib/gobstones/lang/definitions/all.rb +4 -0
- data/lib/gobstones/lang/definitions/definition.rb +32 -0
- data/lib/gobstones/lang/definitions/definition_call.rb +24 -0
- data/lib/gobstones/lang/definitions/function.rb +14 -0
- data/lib/gobstones/lang/definitions/main.rb +26 -0
- data/lib/gobstones/lang/definitions/no_return_statement.rb +15 -0
- data/lib/gobstones/lang/definitions/procedure.rb +44 -0
- data/lib/gobstones/lang/definitions/return_from_function.rb +22 -0
- data/lib/gobstones/lang/definitions/return_from_main.rb +22 -0
- data/lib/gobstones/lang/definitions/var_tuple.rb +26 -0
- data/lib/gobstones/lang/expressions/all.rb +8 -0
- data/lib/gobstones/lang/expressions/arithmetic_expressions.rb +52 -0
- data/lib/gobstones/lang/expressions/boolean_expressions.rb +29 -0
- data/lib/gobstones/lang/expressions/comparison_expressions.rb +45 -0
- data/lib/gobstones/lang/expressions/function_call.rb +15 -0
- data/lib/gobstones/lang/expressions/one_arg_expression.rb +21 -0
- data/lib/gobstones/lang/expressions/parentheses_expression.rb +13 -0
- data/lib/gobstones/lang/expressions/primitive_functions.rb +68 -0
- data/lib/gobstones/lang/expressions/two_arg_expression.rb +34 -0
- data/lib/gobstones/lang/expressions/type_bound_functions.rb +66 -0
- data/lib/gobstones/lang/expressions/var_name.rb +31 -0
- data/lib/gobstones/lang/literals/all.rb +4 -0
- data/lib/gobstones/lang/literals/booleans.rb +109 -0
- data/lib/gobstones/lang/literals/colors.rb +94 -0
- data/lib/gobstones/lang/literals/directions.rb +137 -0
- data/lib/gobstones/lang/literals/literal.rb +63 -0
- data/lib/gobstones/lang/literals/number.rb +49 -0
- data/lib/gobstones/lang/program.rb +32 -0
- data/lib/gobstones/modules/equal_by_class.rb +13 -0
- data/lib/gobstones/parser/ast/ast.rb +210 -0
- data/lib/gobstones/parser/grammar/gobstones.treetop +316 -0
- data/lib/gobstones/parser/parse_error.rb +17 -0
- data/lib/gobstones/parser/treetop_parser.rb +73 -0
- data/lib/gobstones/runner/all.rb +6 -0
- data/lib/gobstones/runner/board.rb +57 -0
- data/lib/gobstones/runner/cell.rb +60 -0
- data/lib/gobstones/runner/errors/all.rb +8 -0
- data/lib/gobstones/runner/errors/boom_error.rb +11 -0
- data/lib/gobstones/runner/errors/definition_not_found_error.rb +19 -0
- data/lib/gobstones/runner/errors/empty_cell_error.rb +11 -0
- data/lib/gobstones/runner/errors/gobstones_runtime_error.rb +11 -0
- data/lib/gobstones/runner/errors/gobstones_type_error.rb +11 -0
- data/lib/gobstones/runner/errors/out_of_board_error.rb +11 -0
- data/lib/gobstones/runner/errors/undefined_variable_error.rb +11 -0
- data/lib/gobstones/runner/errors/wrong_arguments_error.rb +11 -0
- data/lib/gobstones/runner/execution_context.rb +89 -0
- data/lib/gobstones/runner/head.rb +101 -0
- data/lib/gobstones/type_check_result.rb +16 -0
- data/spec/lang/commands/assignments_spec.rb +13 -0
- data/spec/lang/commands/boom_cmd_spec.rb +8 -0
- data/spec/lang/commands/cmd_block_spec.rb +21 -0
- data/spec/lang/commands/if_cmd_spec.rb +49 -0
- data/spec/lang/commands/ir_al_origen_cmd_spec.rb +16 -0
- data/spec/lang/commands/mover_cmd_spec.rb +38 -0
- data/spec/lang/commands/poner_cmd_spec.rb +29 -0
- data/spec/lang/commands/procedure_call_spec.rb +44 -0
- data/spec/lang/commands/procedure_spec.rb +67 -0
- data/spec/lang/commands/repeat_with_cmd_spec.rb +41 -0
- data/spec/lang/commands/sacar_cmd_spec.rb +34 -0
- data/spec/lang/commands/skip_cmd_spec.rb +12 -0
- data/spec/lang/commands/vaciar_tablero_cmd_spec.rb +13 -0
- data/spec/lang/commands/while_cmd_spec.rb +37 -0
- data/spec/lang/expressions/arithmetic_expressions_spec.rb +108 -0
- data/spec/lang/expressions/boolean_expressions_spec.rb +56 -0
- data/spec/lang/expressions/comparison_expressions_spec.rb +285 -0
- data/spec/lang/expressions/primitive_functions_spec.rb +126 -0
- data/spec/lang/expressions/type_bound_functions_spec.rb +39 -0
- data/spec/lang/expressions/var_name_spec.rb +16 -0
- data/spec/lang/literals/booleans_spec.rb +13 -0
- data/spec/lang/literals/colors_spec.rb +13 -0
- data/spec/lang/literals/directions_spec.rb +45 -0
- data/spec/lang/literals/numbers_spec.rb +8 -0
- data/spec/matchers/parse_matcher.rb +84 -0
- data/spec/parser/arithmetic_expressions_spec.rb +129 -0
- data/spec/parser/assignments_spec.rb +39 -0
- data/spec/parser/boolean_expressions_spec.rb +111 -0
- data/spec/parser/command_block_spec.rb +50 -0
- data/spec/parser/data_types_spec.rb +67 -0
- data/spec/parser/function_calls_spec.rb +37 -0
- data/spec/parser/function_definitions_spec.rb +50 -0
- data/spec/parser/gobstones_program_spec.rb +55 -0
- data/spec/parser/if_command_spec.rb +46 -0
- data/spec/parser/main_definition_spec.rb +42 -0
- data/spec/parser/nested_expressions_spec.rb +39 -0
- data/spec/parser/primitive_expressions_spec.rb +105 -0
- data/spec/parser/procedure_calls_spec.rb +36 -0
- data/spec/parser/procedure_definitions_spec.rb +39 -0
- data/spec/parser/repeat_with_command_spec.rb +23 -0
- data/spec/parser/simple_commands_spec.rb +62 -0
- data/spec/parser/treetop_parser_spec.rb +109 -0
- data/spec/parser/var_tuple_spec.rb +30 -0
- data/spec/parser/while_command_spec.rb +30 -0
- data/spec/runner/board_spec.rb +85 -0
- data/spec/runner/cell_spec.rb +72 -0
- data/spec/runner/execution_context_spec.rb +64 -0
- data/spec/runner/head_spec.rb +139 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/type_checker_spec.rb +37 -0
- metadata +242 -0
@@ -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,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
|