gobstones 0.0.1.1 → 0.0.2
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 +4 -4
- data/.gitignore +1 -3
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/CHANGELOG +22 -2
- data/Gemfile +3 -3
- data/Gemfile.lock +32 -27
- data/bin/gobstones +1 -1
- data/examples/.gitkeep +0 -0
- data/gobstones.gemspec +2 -2
- data/lib/gobstones/cli/board_template +1 -1
- data/lib/gobstones/cli/printer.rb +1 -1
- data/lib/gobstones/cli/runner.rb +8 -2
- data/lib/gobstones/extensions/all.rb +2 -1
- data/lib/gobstones/extensions/boolean.rb +1 -1
- data/lib/gobstones/extensions/fixnum.rb +1 -1
- data/lib/gobstones/extensions/string.rb +9 -0
- data/lib/gobstones/lang/all.rb +1 -1
- data/lib/gobstones/lang/commands/all.rb +3 -2
- data/lib/gobstones/lang/commands/boom_cmd.rb +6 -3
- data/lib/gobstones/lang/commands/command_block.rb +1 -1
- data/lib/gobstones/lang/commands/conditional_cmd.rb +5 -1
- data/lib/gobstones/lang/commands/ir_al_origen_cmd.rb +1 -1
- data/lib/gobstones/lang/commands/mover_cmd.rb +4 -2
- data/lib/gobstones/lang/commands/multiple_assignment.rb +35 -0
- data/lib/gobstones/lang/commands/poner_cmd.rb +8 -4
- data/lib/gobstones/lang/commands/procedure_call.rb +1 -9
- data/lib/gobstones/lang/commands/repeat_with_cmd.rb +10 -6
- data/lib/gobstones/lang/commands/sacar_cmd.rb +9 -7
- data/lib/gobstones/lang/commands/{assignments.rb → single_assignment.rb} +5 -3
- data/lib/gobstones/lang/commands/skip_cmd.rb +2 -2
- data/lib/gobstones/lang/commands/vaciar_tablero_cmd.rb +1 -1
- data/lib/gobstones/lang/commands/while_cmd.rb +1 -1
- data/lib/gobstones/lang/definitions/all.rb +1 -1
- data/lib/gobstones/lang/definitions/definition.rb +47 -5
- data/lib/gobstones/lang/definitions/definition_call.rb +17 -5
- data/lib/gobstones/lang/definitions/function.rb +9 -0
- data/lib/gobstones/lang/definitions/main.rb +1 -1
- data/lib/gobstones/lang/definitions/no_return_statement.rb +7 -3
- data/lib/gobstones/lang/definitions/procedure.rb +5 -20
- data/lib/gobstones/lang/definitions/return_from_function.rb +12 -2
- data/lib/gobstones/lang/definitions/return_from_main.rb +5 -1
- data/lib/gobstones/lang/definitions/var_tuple.rb +13 -1
- data/lib/gobstones/lang/expressions/all.rb +1 -1
- data/lib/gobstones/lang/expressions/arithmetic_expressions.rb +1 -1
- data/lib/gobstones/lang/expressions/boolean_expressions.rb +3 -2
- data/lib/gobstones/lang/expressions/enclosed_by_parens_expression.rb +17 -0
- data/lib/gobstones/lang/expressions/function_call.rb +1 -1
- data/lib/gobstones/lang/expressions/one_arg_expression.rb +16 -4
- data/lib/gobstones/lang/expressions/primitive_functions.rb +18 -12
- data/lib/gobstones/lang/expressions/two_arg_expression.rb +7 -3
- data/lib/gobstones/lang/expressions/type_bound_functions.rb +1 -1
- data/lib/gobstones/lang/expressions/var_name.rb +9 -3
- data/lib/gobstones/lang/literals/all.rb +1 -1
- data/lib/gobstones/lang/literals/colors.rb +1 -1
- data/lib/gobstones/lang/literals/literal.rb +31 -21
- data/lib/gobstones/lang/literals/number.rb +1 -1
- data/lib/gobstones/lang/program.rb +13 -4
- data/lib/gobstones/modules/equal_by_class.rb +1 -1
- data/lib/gobstones/parser/ast/ast.rb +12 -8
- data/lib/gobstones/parser/grammar/gobstones.treetop +4 -4
- data/lib/gobstones/parser/parse_error.rb +1 -1
- data/lib/gobstones/runner/all.rb +1 -2
- data/lib/gobstones/runner/board.rb +13 -4
- data/lib/gobstones/runner/cell.rb +10 -0
- data/lib/gobstones/runner/errors/all.rb +1 -1
- data/lib/gobstones/runner/errors/definition_not_found_error.rb +1 -1
- data/lib/gobstones/runner/execution_context.rb +66 -16
- data/lib/gobstones/runner/head.rb +17 -3
- data/spec/gobstones_lang_test_objects.rb +75 -0
- data/spec/lang/commands/boom_cmd_spec.rb +3 -3
- data/spec/lang/commands/cmd_block_spec.rb +14 -14
- data/spec/lang/commands/if_cmd_spec.rb +21 -20
- data/spec/lang/commands/ir_al_origen_cmd_spec.rb +3 -3
- data/spec/lang/commands/mover_cmd_spec.rb +12 -14
- data/spec/lang/commands/multiple_assignment_spec.rb +37 -0
- data/spec/lang/commands/poner_cmd_spec.rb +13 -14
- data/spec/lang/commands/procedure_call_spec.rb +19 -24
- data/spec/lang/commands/procedure_spec.rb +32 -36
- data/spec/lang/commands/repeat_with_cmd_spec.rb +39 -20
- data/spec/lang/commands/sacar_cmd_spec.rb +17 -16
- data/spec/lang/commands/single_assignment_spec.rb +13 -0
- data/spec/lang/commands/skip_cmd_spec.rb +2 -2
- data/spec/lang/commands/vaciar_tablero_cmd_spec.rb +7 -6
- data/spec/lang/commands/while_cmd_spec.rb +21 -15
- data/spec/lang/definitions/no_return_statement_spec.rb +10 -0
- data/spec/lang/definitions/var_tuple_spec.rb +16 -0
- data/spec/lang/expressions/arithmetic_expressions_spec.rb +15 -15
- data/spec/lang/expressions/boolean_expressions_spec.rb +35 -25
- data/spec/lang/expressions/comparison_expressions_spec.rb +25 -28
- data/spec/lang/expressions/enclosed_by_parens_expression_spec.rb +11 -0
- data/spec/lang/expressions/function_call_spec.rb +29 -0
- data/spec/lang/expressions/primitive_functions_spec.rb +60 -62
- data/spec/lang/expressions/type_bound_functions_spec.rb +13 -13
- data/spec/lang/expressions/var_name_spec.rb +20 -8
- data/spec/lang/literals/booleans_spec.rb +5 -7
- data/spec/lang/literals/colors_spec.rb +4 -4
- data/spec/lang/literals/directions_spec.rb +12 -12
- data/spec/lang/literals/numbers_spec.rb +2 -2
- data/spec/matchers/parse_matcher.rb +9 -10
- data/spec/parser/arithmetic_expressions_spec.rb +19 -19
- data/spec/parser/assignments_spec.rb +24 -10
- data/spec/parser/boolean_expressions_spec.rb +18 -18
- data/spec/parser/command_block_spec.rb +17 -19
- data/spec/parser/data_types_spec.rb +23 -23
- data/spec/parser/function_calls_spec.rb +13 -12
- data/spec/parser/function_definitions_spec.rb +13 -18
- data/spec/parser/gobstones_program_spec.rb +15 -15
- data/spec/parser/if_command_spec.rb +13 -12
- data/spec/parser/main_definition_spec.rb +12 -12
- data/spec/parser/nested_expressions_spec.rb +16 -20
- data/spec/parser/primitive_expressions_spec.rb +27 -33
- data/spec/parser/procedure_calls_spec.rb +12 -12
- data/spec/parser/procedure_definitions_spec.rb +10 -16
- data/spec/parser/repeat_with_command_spec.rb +7 -10
- data/spec/parser/simple_commands_spec.rb +10 -10
- data/spec/parser/treetop_parser_spec.rb +11 -10
- data/spec/parser/var_tuple_spec.rb +7 -11
- data/spec/parser/while_command_spec.rb +9 -9
- data/spec/runner/board_spec.rb +23 -27
- data/spec/runner/cell_spec.rb +34 -38
- data/spec/runner/execution_context_spec.rb +38 -24
- data/spec/runner/head_spec.rb +54 -63
- data/spec/spec_helper.rb +4 -1
- data/spec/type_checker_spec.rb +13 -13
- metadata +33 -18
- data/lib/gobstones/lang/expressions/parentheses_expression.rb +0 -13
- data/spec/lang/commands/assignments_spec.rb +0 -13
@@ -2,7 +2,7 @@ require 'gobstones/lang/expressions/arithmetic_expressions'
|
|
2
2
|
require 'gobstones/lang/expressions/boolean_expressions'
|
3
3
|
require 'gobstones/lang/expressions/comparison_expressions'
|
4
4
|
require 'gobstones/lang/expressions/function_call'
|
5
|
-
require 'gobstones/lang/expressions/
|
5
|
+
require 'gobstones/lang/expressions/enclosed_by_parens_expression'
|
6
6
|
require 'gobstones/lang/expressions/primitive_functions'
|
7
7
|
require 'gobstones/lang/expressions/type_bound_functions'
|
8
8
|
require 'gobstones/lang/expressions/var_name'
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'gobstones/lang/expressions/one_arg_expression'
|
2
|
+
require 'gobstones/lang/expressions/two_arg_expression'
|
2
3
|
|
3
4
|
module Gobstones
|
4
5
|
|
@@ -6,8 +7,8 @@ module Gobstones
|
|
6
7
|
|
7
8
|
class Not < OneArgExpression
|
8
9
|
|
9
|
-
def evaluate
|
10
|
-
|
10
|
+
def evaluate(context)
|
11
|
+
with_evaluated_argument_in(context) { |result| result.not }
|
11
12
|
end
|
12
13
|
|
13
14
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'gobstones/lang/expressions/one_arg_expression'
|
2
|
+
|
3
|
+
module Gobstones
|
4
|
+
|
5
|
+
module Lang
|
6
|
+
|
7
|
+
class EnclosedByParensExpression < OneArgExpression
|
8
|
+
|
9
|
+
def evaluate(context)
|
10
|
+
with_evaluated_argument_in(context) { |result| result }
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -1,17 +1,29 @@
|
|
1
|
+
require 'gobstones/modules/equal_by_class'
|
2
|
+
|
1
3
|
module Gobstones
|
2
4
|
|
3
5
|
module Lang
|
4
6
|
|
5
7
|
class OneArgExpression
|
6
8
|
|
7
|
-
|
9
|
+
include Gobstones::EqualByClass
|
10
|
+
|
11
|
+
attr_reader :argument
|
8
12
|
|
9
|
-
def initialize(
|
10
|
-
@
|
13
|
+
def initialize(argument)
|
14
|
+
@argument = argument
|
11
15
|
end
|
12
16
|
|
13
17
|
def ==(other)
|
14
|
-
|
18
|
+
super(other) && argument == other.argument
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate(context)
|
22
|
+
raise 'subclass responsibility'
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_evaluated_argument_in(context, &block)
|
26
|
+
yield argument.evaluate(context)
|
15
27
|
end
|
16
28
|
|
17
29
|
end
|
@@ -10,7 +10,9 @@ module Gobstones
|
|
10
10
|
class NroBolitas < OneArgExpression
|
11
11
|
|
12
12
|
def evaluate(context)
|
13
|
-
|
13
|
+
with_evaluated_argument_in(context) do |result|
|
14
|
+
context.head.number_of_balls(result).to_gbs_num
|
15
|
+
end
|
14
16
|
end
|
15
17
|
|
16
18
|
end
|
@@ -18,7 +20,9 @@ module Gobstones
|
|
18
20
|
class HayBolitas < OneArgExpression
|
19
21
|
|
20
22
|
def evaluate(context)
|
21
|
-
|
23
|
+
with_evaluated_argument_in(context) do |result|
|
24
|
+
context.head.are_there_balls?(result).to_gbs_bool
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
28
|
end
|
@@ -26,43 +30,45 @@ module Gobstones
|
|
26
30
|
class PuedeMover < OneArgExpression
|
27
31
|
|
28
32
|
def evaluate(context)
|
29
|
-
|
33
|
+
with_evaluated_argument_in(context) do |result|
|
34
|
+
context.head.can_move?(result).to_gbs_bool
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
|
-
def
|
38
|
+
def argument_type
|
33
39
|
:Direction
|
34
40
|
end
|
35
41
|
|
36
42
|
def type_check
|
37
|
-
Gobstones::TypeCheckResult.new
|
43
|
+
Gobstones::TypeCheckResult.new argument_type, argument.return_type
|
38
44
|
end
|
39
45
|
|
40
46
|
end
|
41
47
|
|
42
48
|
class Siguiente < OneArgExpression
|
43
49
|
|
44
|
-
def evaluate(context
|
45
|
-
|
50
|
+
def evaluate(context)
|
51
|
+
with_evaluated_argument_in(context) { |result| result.next }
|
46
52
|
end
|
47
53
|
|
48
54
|
end
|
49
55
|
|
50
56
|
class Previo < OneArgExpression
|
51
57
|
|
52
|
-
def evaluate(context
|
53
|
-
|
58
|
+
def evaluate(context)
|
59
|
+
with_evaluated_argument_in(context) { |result| result.previous }
|
54
60
|
end
|
55
61
|
|
56
62
|
end
|
57
63
|
|
58
64
|
class Opuesto < OneArgExpression
|
59
65
|
|
60
|
-
def evaluate(context
|
61
|
-
|
66
|
+
def evaluate(context)
|
67
|
+
with_evaluated_argument_in(context) { |result| result.opposite }
|
62
68
|
end
|
63
69
|
|
64
70
|
end
|
65
71
|
|
66
72
|
end
|
67
73
|
|
68
|
-
end
|
74
|
+
end
|
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'gobstones/modules/equal_by_class'
|
2
|
+
|
1
3
|
module Gobstones
|
2
4
|
|
3
5
|
module Lang
|
4
6
|
|
5
7
|
class TwoArgExpression
|
6
8
|
|
9
|
+
include Gobstones::EqualByClass
|
10
|
+
|
7
11
|
attr_reader :left_expr, :right_expr
|
8
12
|
|
9
13
|
def self.evaluates_with(selector)
|
@@ -22,9 +26,9 @@ module Gobstones
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def ==(other)
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
super(other) &&
|
30
|
+
self.left_expr == other.left_expr &&
|
31
|
+
self.right_expr == other.right_expr
|
28
32
|
end
|
29
33
|
|
30
34
|
end
|
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'gobstones/modules/equal_by_class'
|
2
|
+
|
1
3
|
module Gobstones
|
2
4
|
|
3
5
|
module Lang
|
4
6
|
|
5
7
|
class VarName
|
6
8
|
|
9
|
+
include Gobstones::EqualByClass
|
10
|
+
|
7
11
|
attr_reader :name
|
8
12
|
|
9
13
|
def initialize(name)
|
@@ -11,11 +15,9 @@ module Gobstones
|
|
11
15
|
end
|
12
16
|
|
13
17
|
def ==(var)
|
14
|
-
|
18
|
+
super(var) && self.name == var.name
|
15
19
|
end
|
16
20
|
|
17
|
-
alias eql? ==
|
18
|
-
|
19
21
|
def hash
|
20
22
|
@name.hash
|
21
23
|
end
|
@@ -24,6 +26,10 @@ module Gobstones
|
|
24
26
|
context.get self
|
25
27
|
end
|
26
28
|
|
29
|
+
def named?(a_name)
|
30
|
+
name == a_name
|
31
|
+
end
|
32
|
+
|
27
33
|
end
|
28
34
|
|
29
35
|
end
|
@@ -22,26 +22,6 @@ module Gobstones
|
|
22
22
|
raise 'subclass responsibility'
|
23
23
|
end
|
24
24
|
|
25
|
-
def if_true(block, context)
|
26
|
-
raise Gobstones::Runner::GobstonesTypeError, "#{self} is not a boolean"
|
27
|
-
end
|
28
|
-
|
29
|
-
def if_false(block, context)
|
30
|
-
raise Gobstones::Runner::GobstonesTypeError, "#{self} is not a boolean"
|
31
|
-
end
|
32
|
-
|
33
|
-
def is_true?
|
34
|
-
raise Gobstones::Runner::GobstonesTypeError, "#{self} is not a boolean"
|
35
|
-
end
|
36
|
-
|
37
|
-
def same_type_as(other)
|
38
|
-
self.return_type == other.return_type
|
39
|
-
end
|
40
|
-
|
41
|
-
def return_type
|
42
|
-
raise 'subclass responsibility'
|
43
|
-
end
|
44
|
-
|
45
25
|
OPERATORS_MAPPING = {
|
46
26
|
:equal => :==,
|
47
27
|
:not_equal => '!='.to_sym,
|
@@ -56,8 +36,38 @@ module Gobstones
|
|
56
36
|
end
|
57
37
|
end
|
58
38
|
|
39
|
+
def same_type_as(other)
|
40
|
+
self.return_type == other.return_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def return_type
|
44
|
+
raise 'subclass responsibility'
|
45
|
+
end
|
46
|
+
|
47
|
+
def if_true(block, context)
|
48
|
+
not_boolean_type_error
|
49
|
+
end
|
50
|
+
|
51
|
+
def if_false(block, context)
|
52
|
+
not_boolean_type_error
|
53
|
+
end
|
54
|
+
|
55
|
+
def is_true?
|
56
|
+
not_boolean_type_error
|
57
|
+
end
|
58
|
+
|
59
|
+
def not
|
60
|
+
not_boolean_type_error
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def not_boolean_type_error
|
66
|
+
raise Gobstones::Runner::GobstonesTypeError, "#{self} is not a boolean"
|
67
|
+
end
|
68
|
+
|
59
69
|
end
|
60
70
|
|
61
71
|
end
|
62
72
|
|
63
|
-
end
|
73
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'gobstones/runner/execution_context'
|
2
|
+
require 'gobstones/modules/equal_by_class'
|
2
3
|
|
3
4
|
module Gobstones
|
4
5
|
|
@@ -6,6 +7,8 @@ module Gobstones
|
|
6
7
|
|
7
8
|
class Program
|
8
9
|
|
10
|
+
include Gobstones::EqualByClass
|
11
|
+
|
9
12
|
attr_reader :definitions, :main_definition
|
10
13
|
|
11
14
|
def initialize(definitions, main_definition)
|
@@ -14,15 +17,21 @@ module Gobstones
|
|
14
17
|
end
|
15
18
|
|
16
19
|
def ==(other)
|
17
|
-
|
20
|
+
super(other) &&
|
18
21
|
self.definitions == other.definitions &&
|
19
22
|
self.main_definition == other.main_definition
|
20
23
|
end
|
21
24
|
|
22
25
|
def evaluate
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
create_context.tap do |context|
|
27
|
+
main_definition.evaluate context
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def create_context
|
34
|
+
Gobstones::Runner::ProgramExecutionContext.for self
|
26
35
|
end
|
27
36
|
|
28
37
|
end
|
@@ -53,9 +53,9 @@ module Gobstones
|
|
53
53
|
sub_exps.elements.inject(left.value) do |memo, node|
|
54
54
|
case node.op.text_value
|
55
55
|
when '+'
|
56
|
-
|
56
|
+
Add.new memo, node.right.value
|
57
57
|
when '-'
|
58
|
-
|
58
|
+
Sub.new memo, node.right.value
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -67,7 +67,7 @@ module Gobstones
|
|
67
67
|
else
|
68
68
|
# it is a nested exp
|
69
69
|
sub_exps.elements.inject(left.value) do |memo, node|
|
70
|
-
|
70
|
+
Mul.new memo, node.right.value
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -87,7 +87,7 @@ module Gobstones
|
|
87
87
|
else
|
88
88
|
# it is a nested exp
|
89
89
|
sub_exps.elements.inject(left.value) do |memo, node|
|
90
|
-
|
90
|
+
Pow.new memo, node.right.value
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -105,7 +105,7 @@ module Gobstones
|
|
105
105
|
ast_node(:AndExprNode) { And.new left.value, right.value }
|
106
106
|
ast_node(:OrExprNode) { Or.new left.value, right.value }
|
107
107
|
|
108
|
-
ast_node(:ParenthesesExprNode) {
|
108
|
+
ast_node(:ParenthesesExprNode) { EnclosedByParensExpression.new gexp.value }
|
109
109
|
ast_node(:FuncCallNode) do
|
110
110
|
FunctionCall.new func_name.text_value, gexp_tuple.value
|
111
111
|
end
|
@@ -137,8 +137,12 @@ module Gobstones
|
|
137
137
|
SingleAssignment.new var_name.value, gexp.value
|
138
138
|
end
|
139
139
|
|
140
|
+
ast_node :MultipleAssignmentNode do
|
141
|
+
MultipleAssignment.new var_tuple.value, gexp.value
|
142
|
+
end
|
143
|
+
|
140
144
|
ast_node :CmdBlockNode do
|
141
|
-
|
145
|
+
CommandBlock.new create_commands(commands)
|
142
146
|
end
|
143
147
|
|
144
148
|
ast_node :IfCmdNode do
|
@@ -177,7 +181,7 @@ module Gobstones
|
|
177
181
|
end
|
178
182
|
|
179
183
|
ast_node(:FunctionNode) do
|
180
|
-
cmd_block =
|
184
|
+
cmd_block = CommandBlock.new create_commands(commands)
|
181
185
|
Function.new func_name.text_value, var_tuple.value,
|
182
186
|
cmd_block, func_return.value
|
183
187
|
end
|
@@ -191,7 +195,7 @@ module Gobstones
|
|
191
195
|
end
|
192
196
|
|
193
197
|
ast_node(:MainDefNode) do
|
194
|
-
cmd_block =
|
198
|
+
cmd_block = CommandBlock.new create_commands(commands)
|
195
199
|
return_st = ret.empty? ? NoReturnStatement.new : ret.value
|
196
200
|
Main.new cmd_block, return_st
|
197
201
|
end
|
@@ -53,7 +53,7 @@ grammar Gobstones
|
|
53
53
|
rule simple_cmd
|
54
54
|
skip_cmd / boom_cmd / poner_cmd / sacar_cmd / mover_cmd /
|
55
55
|
ir_al_origen_cmd / vaciar_tablero_cmd / proc_call /
|
56
|
-
cmd_block / single_assignment
|
56
|
+
cmd_block / single_assignment / multiple_assignment
|
57
57
|
end
|
58
58
|
|
59
59
|
rule skip_cmd
|
@@ -96,9 +96,9 @@ grammar Gobstones
|
|
96
96
|
var_name space* ':=' space* gexp <SingleAssignmentNode>
|
97
97
|
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
rule multiple_assignment
|
100
|
+
var_tuple space* ':=' space* gexp <MultipleAssignmentNode>
|
101
|
+
end
|
102
102
|
|
103
103
|
rule complex_cmd
|
104
104
|
if_cmd / while_cmd / repeat_with_cmd # / case_cmd
|