blockly_interpreter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +12 -0
  5. data/COPYING +8 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +91 -0
  8. data/Guardfile +22 -0
  9. data/README.md +72 -0
  10. data/Rakefile +10 -0
  11. data/app/assets/javascripts/blockly_interpreter/extension_blocks.js +1 -0
  12. data/app/assets/javascripts/blockly_interpreter/extension_blocks/dates.js +16 -0
  13. data/app/assets/javascripts/blockly_interpreter/extension_blocks/debugging.js +17 -0
  14. data/app/assets/javascripts/blockly_interpreter/extension_blocks/lists.js +45 -0
  15. data/app/assets/javascripts/blockly_interpreter/extension_blocks/logic.js +274 -0
  16. data/app/assets/javascripts/blockly_interpreter/extension_blocks/text.js.erb +21 -0
  17. data/bin/_guard-core +16 -0
  18. data/bin/console +14 -0
  19. data/bin/guard +16 -0
  20. data/bin/rake +16 -0
  21. data/bin/setup +7 -0
  22. data/blockly_interpreter.gemspec +33 -0
  23. data/exe/blocklyi +13 -0
  24. data/exe/rublocklyc +31 -0
  25. data/lib/blockly_interpreter.rb +25 -0
  26. data/lib/blockly_interpreter/block.rb +113 -0
  27. data/lib/blockly_interpreter/block_library.rb +14 -0
  28. data/lib/blockly_interpreter/console_interpreter.rb +15 -0
  29. data/lib/blockly_interpreter/core_blocks.rb +55 -0
  30. data/lib/blockly_interpreter/core_blocks/arithmetic_operator_block.rb +24 -0
  31. data/lib/blockly_interpreter/core_blocks/boolean_block.rb +23 -0
  32. data/lib/blockly_interpreter/core_blocks/comparison_operator_block.rb +50 -0
  33. data/lib/blockly_interpreter/core_blocks/for_block.rb +68 -0
  34. data/lib/blockly_interpreter/core_blocks/for_each_block.rb +42 -0
  35. data/lib/blockly_interpreter/core_blocks/get_variable_block.rb +19 -0
  36. data/lib/blockly_interpreter/core_blocks/if_block.rb +105 -0
  37. data/lib/blockly_interpreter/core_blocks/lists_create_empty_block.rb +17 -0
  38. data/lib/blockly_interpreter/core_blocks/lists_create_with_block.rb +48 -0
  39. data/lib/blockly_interpreter/core_blocks/lists_get_index_block.rb +95 -0
  40. data/lib/blockly_interpreter/core_blocks/logic_negate_block.rb +20 -0
  41. data/lib/blockly_interpreter/core_blocks/logical_operator_block.rb +22 -0
  42. data/lib/blockly_interpreter/core_blocks/number_block.rb +23 -0
  43. data/lib/blockly_interpreter/core_blocks/procedure_block.rb +49 -0
  44. data/lib/blockly_interpreter/core_blocks/procedures_call_no_return_block.rb +21 -0
  45. data/lib/blockly_interpreter/core_blocks/procedures_call_return_block.rb +21 -0
  46. data/lib/blockly_interpreter/core_blocks/procedures_def_no_return_block.rb +31 -0
  47. data/lib/blockly_interpreter/core_blocks/procedures_def_return_block.rb +64 -0
  48. data/lib/blockly_interpreter/core_blocks/procedures_if_return_block.rb +45 -0
  49. data/lib/blockly_interpreter/core_blocks/repeat_times_block.rb +33 -0
  50. data/lib/blockly_interpreter/core_blocks/set_variable_block.rb +24 -0
  51. data/lib/blockly_interpreter/core_blocks/text_block.rb +23 -0
  52. data/lib/blockly_interpreter/core_blocks/text_change_case_block.rb +32 -0
  53. data/lib/blockly_interpreter/core_blocks/text_join_block.rb +50 -0
  54. data/lib/blockly_interpreter/dsl.rb +291 -0
  55. data/lib/blockly_interpreter/dsl_generator.rb +147 -0
  56. data/lib/blockly_interpreter/engine.rb +8 -0
  57. data/lib/blockly_interpreter/execution_context.rb +72 -0
  58. data/lib/blockly_interpreter/extension_blocks.rb +25 -0
  59. data/lib/blockly_interpreter/extension_blocks/date_today_block.rb +17 -0
  60. data/lib/blockly_interpreter/extension_blocks/debug_message_block.rb +18 -0
  61. data/lib/blockly_interpreter/extension_blocks/lists_append_block.rb +34 -0
  62. data/lib/blockly_interpreter/extension_blocks/lists_concat_block.rb +37 -0
  63. data/lib/blockly_interpreter/extension_blocks/lists_include_operator_block.rb +52 -0
  64. data/lib/blockly_interpreter/extension_blocks/object_present_block.rb +21 -0
  65. data/lib/blockly_interpreter/extension_blocks/switch_block.rb +107 -0
  66. data/lib/blockly_interpreter/extension_blocks/text_inflect_block.rb +27 -0
  67. data/lib/blockly_interpreter/generic_block_dsl_generator.rb +64 -0
  68. data/lib/blockly_interpreter/interpreter.rb +25 -0
  69. data/lib/blockly_interpreter/parser.rb +117 -0
  70. data/lib/blockly_interpreter/program.rb +51 -0
  71. data/lib/blockly_interpreter/program_cache.rb +19 -0
  72. data/lib/blockly_interpreter/test_helper.rb +98 -0
  73. data/lib/blockly_interpreter/version.rb +3 -0
  74. metadata +272 -0
@@ -0,0 +1,50 @@
1
+ class BlocklyInterpreter::CoreBlocks::ComparisonOperatorBlock < BlocklyInterpreter::Block
2
+ self.block_type = :logic_compare
3
+
4
+ def value(execution_context)
5
+ a = values['A'].value(execution_context)
6
+ b = values['B'].value(execution_context)
7
+
8
+ a, b = cast_values(a, b)
9
+
10
+ begin
11
+ case fields['OP']
12
+ when 'EQ' then a == b
13
+ when 'NEQ' then a != b
14
+ when 'LT' then a < b
15
+ when 'GT' then a > b
16
+ when 'LTE' then a <= b
17
+ when 'GTE' then a >= b
18
+ end
19
+ rescue
20
+ false
21
+ end
22
+ end
23
+
24
+ def to_dsl
25
+ BlocklyInterpreter::DSL::BinaryOperationDSLGenerator.new(self, "logic_compare").dsl
26
+ end
27
+
28
+ module DSLMethods
29
+ def logic_compare(op, a = nil, b = nil, &proc)
30
+ @blocks << BlocklyInterpreter::DSL::BinaryOperationBlockBuilder.new("logic_compare", op, a, b).tap do |builder|
31
+ builder.instance_exec(&proc) if block_given?
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def cast_values(a, b)
39
+ return a, b if a.nil? && b.nil?
40
+ return a, b if a.is_a?(b.class) || b.is_a?(a.class)
41
+
42
+ if a.is_a?(Float) || b.is_a?(Float)
43
+ [a.to_f, b.to_f]
44
+ elsif a.is_a?(Integer) || b.is_a?(Integer)
45
+ [a.to_i, b.to_i]
46
+ else
47
+ [a, b]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,68 @@
1
+ class BlocklyInterpreter::CoreBlocks::ForBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = 'controls_for'
4
+
5
+ def execute_statement(execution_context)
6
+ from = values['FROM'].value(execution_context).to_i
7
+ to = values['TO'].value(execution_context).to_i
8
+ by = values['BY'].value(execution_context).to_i
9
+
10
+ from.step(by: by, to: to) do |i|
11
+ execution_context.set_variable(fields['VAR'], i)
12
+ execution_context.execute(statements['DO'])
13
+ end
14
+ end
15
+
16
+ def to_dsl
17
+ method_call_with_possible_block('controls_for', fields['VAR'].inspect, [
18
+ method_call_with_block_or_nothing('from', '', values['FROM']),
19
+ method_call_with_block_or_nothing('to', '', values['TO']),
20
+ method_call_with_block_or_nothing('by', '', values['BY']),
21
+ method_call_with_block_or_nothing('action', '', statements['DO'])
22
+ ])
23
+ end
24
+
25
+ module DSLMethods
26
+ class ForBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
27
+ def initialize(block_type, var_name)
28
+ super(block_type)
29
+ field :VAR, var_name
30
+ by(1)
31
+ end
32
+
33
+ def from(val = nil, &proc)
34
+ proc ||= Proc.new { math_number val }
35
+ value "FROM", &proc
36
+ end
37
+
38
+ def to(val = nil, &proc)
39
+ proc ||= Proc.new { math_number val }
40
+ value "TO", &proc
41
+ end
42
+
43
+ def by(val = nil, &proc)
44
+ proc ||= Proc.new { math_number val }
45
+ @by_proc = proc
46
+ end
47
+
48
+ def action(&proc)
49
+ statement "DO", &proc
50
+ end
51
+
52
+ def to_xml(node)
53
+ value "BY", &@by_proc
54
+ super
55
+ end
56
+ end
57
+
58
+ def controls_for(var_name, from = nil, to = nil, by = nil, &proc)
59
+ @blocks << BlocklyInterpreter::CoreBlocks::ForBlock::DSLMethods::ForBlockBuilder.new("controls_for", var_name).tap do |builder|
60
+ builder.from(from) if from
61
+ builder.to(to) if to
62
+ builder.by(by) if by
63
+
64
+ builder.instance_exec(&proc)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,42 @@
1
+ class BlocklyInterpreter::CoreBlocks::ForEachBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = 'controls_forEach'
4
+
5
+ def execute_statement(execution_context)
6
+ list = values['LIST'].value(execution_context)
7
+ list.each do |item|
8
+ execution_context.set_variable(fields['VAR'], item)
9
+ execution_context.execute(statements['DO'])
10
+ end
11
+ end
12
+
13
+ def to_dsl
14
+ method_call_with_possible_block("for_each", fields['VAR'].inspect, [
15
+ method_call_with_block_or_nothing("list", "", values['LIST']),
16
+ method_call_with_block_or_nothing("action", "", statements['DO'])
17
+ ].compact)
18
+ end
19
+
20
+ module DSLMethods
21
+ class ForEachBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
22
+ def initialize(block_type, var_name)
23
+ super(block_type)
24
+ field :VAR, var_name
25
+ end
26
+
27
+ def list(&proc)
28
+ value "LIST", &proc
29
+ end
30
+
31
+ def action(&proc)
32
+ statement "DO", &proc
33
+ end
34
+ end
35
+
36
+ def for_each(var_name, &proc)
37
+ @blocks << BlocklyInterpreter::CoreBlocks::ForEachBlock::DSLMethods::ForEachBlockBuilder.new("controls_forEach", var_name).tap do |builder|
38
+ builder.instance_exec(&proc)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ class BlocklyInterpreter::CoreBlocks::GetVariableBlock < BlocklyInterpreter::Block
2
+ self.block_type = :variables_get
3
+
4
+ def value(execution_context)
5
+ execution_context.get_variable(fields['VAR'])
6
+ end
7
+
8
+ def to_dsl
9
+ "variables_get #{fields['VAR'].inspect}"
10
+ end
11
+
12
+ module DSLMethods
13
+ def variables_get(name)
14
+ block :variables_get do
15
+ field :VAR, name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,105 @@
1
+ class BlocklyInterpreter::CoreBlocks::IfBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :controls_if
4
+
5
+ class Conditional
6
+ attr_reader :predicate_block, :action_block
7
+
8
+ def initialize(if_block, num)
9
+ @predicate_block = if_block.values["IF#{num}"]
10
+ @action_block = if_block.statements["DO#{num}"]
11
+ end
12
+
13
+ def matches?(execution_context)
14
+ predicate_block.value(execution_context).present?
15
+ end
16
+
17
+ def execute_statements(execution_context)
18
+ execution_context.execute(action_block)
19
+ end
20
+ end
21
+
22
+ def predicates
23
+ @predicates ||= begin
24
+ (0..elseif_count).map { |num| Conditional.new(self, num) }
25
+ end
26
+ end
27
+
28
+ def elseif_count
29
+ @elseif_count ||= mutation.try(:[], 'elseif').try(:to_i) || 0
30
+ end
31
+
32
+ def else_block
33
+ statements['ELSE']
34
+ end
35
+
36
+ def execute_statement(execution_context)
37
+ match = matching_predicate(execution_context)
38
+
39
+ if match
40
+ match.execute_statements(execution_context)
41
+ elsif else_block
42
+ execution_context.execute(else_block)
43
+ end
44
+ end
45
+
46
+ def matching_predicate(execution_context)
47
+ predicates.detect { |predicate| predicate.matches? execution_context }
48
+ end
49
+
50
+ def predicate_action_dsl(num)
51
+ [
52
+ method_call_with_possible_block("predicate", "", values["IF#{num}"]),
53
+ method_call_with_possible_block("action", "", statements["DO#{num}"])
54
+ ]
55
+ end
56
+
57
+ def to_dsl
58
+ block_contents = predicate_action_dsl(0) + (1..elseif_count).flat_map do |num|
59
+ method_call_with_possible_block("elsif_clause", "", predicate_action_dsl(num))
60
+ end
61
+ block_contents << method_call_with_block_or_nothing("else_action", "", else_block)
62
+
63
+ method_call_with_block_or_nothing("controls_if", "", block_contents.compact)
64
+ end
65
+
66
+ module DSLMethods
67
+ class IfBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
68
+ attr_reader :predicate_number
69
+
70
+ def initialize(block_type)
71
+ super
72
+ @predicate_number = 0
73
+ end
74
+
75
+ def predicate(&proc)
76
+ value "IF#{predicate_number}", &proc
77
+ end
78
+
79
+ def action(&proc)
80
+ statement "DO#{predicate_number}", &proc
81
+ end
82
+
83
+ def elsif_clause(&proc)
84
+ @predicate_number += 1
85
+ instance_exec(&proc)
86
+ end
87
+
88
+ def else_action(&proc)
89
+ mutation_attr "else", 1
90
+ statement "ELSE", &proc
91
+ end
92
+
93
+ def to_xml(node)
94
+ mutation_attr("elseif", @predicate_number) if @predicate_number > 0
95
+ super
96
+ end
97
+ end
98
+
99
+ def controls_if(&proc)
100
+ @blocks << BlocklyInterpreter::CoreBlocks::IfBlock::DSLMethods::IfBlockBuilder.new("controls_if").tap do |builder|
101
+ builder.instance_exec(&proc)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,17 @@
1
+ class BlocklyInterpreter::CoreBlocks::ListsCreateEmptyBlock < BlocklyInterpreter::Block
2
+ self.block_type = :lists_create_empty
3
+
4
+ def value(execution_context)
5
+ []
6
+ end
7
+
8
+ def to_dsl
9
+ "lists_create_empty"
10
+ end
11
+
12
+ module DSLMethods
13
+ def lists_create_empty
14
+ block :lists_create_empty
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ class BlocklyInterpreter::CoreBlocks::ListsCreateWithBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :lists_create_with
4
+
5
+ def item_count
6
+ @item_count ||= mutation.try(:[], 'items').try(:to_i) || 0
7
+ end
8
+
9
+ def items
10
+ @items ||= (0...item_count).map do |i|
11
+ values["ADD#{i}"]
12
+ end.compact
13
+ end
14
+
15
+ def value(execution_context)
16
+ items.map { |item| item.value(execution_context) }
17
+ end
18
+
19
+ def to_dsl
20
+ item_dsls = items.map { |item| method_call_with_possible_block("item", "", item) }
21
+ method_call_with_possible_block("lists_create_with", "", item_dsls)
22
+ end
23
+
24
+ module DSLMethods
25
+ class ListsCreateWithBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
26
+ def initialize(block_type)
27
+ super
28
+ @item_number = 0
29
+ end
30
+
31
+ def item(&proc)
32
+ value("ADD#{@item_number}", &proc)
33
+ @item_number += 1
34
+ end
35
+
36
+ def to_xml(node)
37
+ mutation_attr("items", @item_number)
38
+ super
39
+ end
40
+ end
41
+
42
+ def lists_create_with(&proc)
43
+ @blocks << BlocklyInterpreter::CoreBlocks::ListsCreateWithBlock::DSLMethods::ListsCreateWithBlockBuilder.new('lists_create_with').tap do |builder|
44
+ builder.instance_exec(&proc)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,95 @@
1
+ class BlocklyInterpreter::CoreBlocks::ListsGetIndexBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :lists_getIndex
4
+
5
+ def is_statement?
6
+ mutation.try(:[], 'statement') == 'true'
7
+ end
8
+
9
+ def execute_statement(execution_context)
10
+ return unless is_statement?
11
+ value(execution_context)
12
+ end
13
+
14
+ def value(execution_context)
15
+ list = values['VALUE'].value(execution_context)
16
+ return unless list
17
+
18
+ list_index = index(execution_context, list)
19
+
20
+ value_at_index = list[list_index]
21
+ list.delete_at(list_index) if %w(GET_REMOVE REMOVE).include?(fields['MODE'])
22
+ value_at_index
23
+ end
24
+
25
+ def to_dsl
26
+ case fields['WHERE']
27
+ when 'FIRST', 'LAST', 'RANDOM'
28
+ method_call_with_possible_block("lists_get_#{fields['WHERE'].downcase}", "", values['VALUE'])
29
+ else
30
+ block_contents = [
31
+ method_call_with_block_or_nothing('at', '', values['AT']),
32
+ method_call_with_block_or_nothing('list', '', values['VALUE'])
33
+ ].compact
34
+
35
+ method_call_with_possible_block("lists_get_index", "mode: #{fields["MODE"].inspect}, where: #{fields["WHERE"].inspect}", block_contents)
36
+ end
37
+ end
38
+
39
+ module DSLMethods
40
+ class ListsGetIndexBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
41
+ def at(index = nil, &proc)
42
+ proc ||= Proc.new { math_number index }
43
+ @has_at = true
44
+ value 'AT', &proc
45
+ end
46
+
47
+ def list(&proc)
48
+ value 'VALUE', &proc
49
+ end
50
+
51
+ def to_xml(node)
52
+ mutation_attr('statement', (@fields["MODE"] == 'REMOVE').to_s)
53
+ mutation_attr('at', (!!@has_at).to_s)
54
+ super
55
+ end
56
+ end
57
+
58
+ def lists_get_first(&proc)
59
+ lists_get_index where: 'FIRST' do
60
+ list &proc
61
+ end
62
+ end
63
+
64
+ def lists_get_last(&proc)
65
+ lists_get_index where: 'LAST' do
66
+ list &proc
67
+ end
68
+ end
69
+
70
+ def lists_get_random(&proc)
71
+ lists_get_index where: 'RANDOM' do
72
+ list &proc
73
+ end
74
+ end
75
+
76
+ def lists_get_index(mode: 'GET', where: 'FROM_START', &proc)
77
+ @blocks << BlocklyInterpreter::CoreBlocks::ListsGetIndexBlock::DSLMethods::ListsGetIndexBlockBuilder.new("lists_getIndex").tap do |builder|
78
+ builder.field 'MODE', mode
79
+ builder.field 'WHERE', where
80
+ builder.instance_exec(&proc)
81
+ end
82
+ end
83
+ end
84
+
85
+ private
86
+ def index(execution_context, list)
87
+ case fields['WHERE']
88
+ when 'FIRST' then 0
89
+ when 'LAST' then list.size - 1
90
+ when 'FROM_START' then values['AT'].value(execution_context) - 1
91
+ when 'FROM_END' then list.size - values['AT'].value(execution_context)
92
+ when 'RANDOM' then rand(list.size)
93
+ end
94
+ end
95
+ end