blockly_interpreter 0.2.0

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 (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