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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d3824303297d44e4ce4640333b83678e1bb5120
4
+ data.tar.gz: aa31793222b180d74e9df43699108c8b6747f524
5
+ SHA512:
6
+ metadata.gz: fb39b11fc79c8cc234cf2fd193dcc57f78f9b68db53d81c432b906963663cdb11e0dce89f0dc3bff2349bb3315f9e79dfe1fd496b357f04144bcb17e58046d84
7
+ data.tar.gz: 6f1566950f8a85f5d9421bfac75fe71155148ebdd6341b244f98b756800a5ff14d94ed13b649887c534a1df9d69f00513e0c78d37bb07eb3c78904e8ee65452e
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ before_install: gem install bundler -v 1.10.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ ## Version 0.2.0 - August 16, 2016
2
+
3
+ * Initial public release.
4
+ * Add `to_xml` and `to_xml_document` methods to `BlocklyInterpreter::Program`, and `to_xml_element` to `BlocklyInterpreter::Block`.
5
+
6
+ ## Version 0.1.1 - June 20, 2016
7
+
8
+ * Expose the helper classes from test_helper to the outside world (although not by default) so that code depending on this gem can use them
9
+
10
+ ## Version 0.1.0 - June 8, 2016
11
+
12
+ * Initial internal release.
data/COPYING ADDED
@@ -0,0 +1,8 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2015-2016 PatientsLikeMe, Inc.
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in blockly_interpreter.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ blockly_interpreter (0.2.0)
5
+ activesupport
6
+ nokogiri
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.2.6)
12
+ i18n (~> 0.7)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.3, >= 0.3.4)
16
+ tzinfo (~> 1.1)
17
+ ansi (1.5.0)
18
+ builder (3.2.2)
19
+ coderay (1.1.1)
20
+ ffi (1.9.10)
21
+ formatador (0.2.5)
22
+ guard (2.13.0)
23
+ formatador (>= 0.2.4)
24
+ listen (>= 2.7, <= 4.0)
25
+ lumberjack (~> 1.0)
26
+ nenv (~> 0.1)
27
+ notiffany (~> 0.0)
28
+ pry (>= 0.9.12)
29
+ shellany (~> 0.0)
30
+ thor (>= 0.18.1)
31
+ guard-compat (1.2.1)
32
+ guard-minitest (2.4.4)
33
+ guard-compat (~> 1.2)
34
+ minitest (>= 3.0)
35
+ i18n (0.7.0)
36
+ json (1.8.3)
37
+ listen (3.0.6)
38
+ rb-fsevent (>= 0.9.3)
39
+ rb-inotify (>= 0.9.7)
40
+ lumberjack (1.0.10)
41
+ method_source (0.8.2)
42
+ mini_portile2 (2.1.0)
43
+ minitest (5.8.4)
44
+ minitest-reporters (1.1.8)
45
+ ansi
46
+ builder
47
+ minitest (>= 5.0)
48
+ ruby-progressbar
49
+ nenv (0.3.0)
50
+ nokogiri (1.6.8)
51
+ mini_portile2 (~> 2.1.0)
52
+ pkg-config (~> 1.1.7)
53
+ notiffany (0.0.8)
54
+ nenv (~> 0.1)
55
+ shellany (~> 0.0)
56
+ pkg-config (1.1.7)
57
+ pry (0.10.3)
58
+ coderay (~> 1.1.0)
59
+ method_source (~> 0.8.1)
60
+ slop (~> 3.4)
61
+ rake (10.5.0)
62
+ rb-fsevent (0.9.7)
63
+ rb-inotify (0.9.7)
64
+ ffi (>= 0.5.0)
65
+ ruby-progressbar (1.7.5)
66
+ shellany (0.0.1)
67
+ slop (3.6.0)
68
+ terminal-notifier-guard (1.7.0)
69
+ thor (0.19.1)
70
+ thread_safe (0.3.5)
71
+ timecop (0.8.0)
72
+ tzinfo (1.2.2)
73
+ thread_safe (~> 0.1)
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ blockly_interpreter!
80
+ bundler (~> 1.10)
81
+ guard
82
+ guard-minitest
83
+ minitest
84
+ minitest-reporters
85
+ pry
86
+ rake (~> 10.0)
87
+ terminal-notifier-guard
88
+ timecop
89
+
90
+ BUNDLED WITH
91
+ 1.10.5
data/Guardfile ADDED
@@ -0,0 +1,22 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ watch(%r{^test/(.*)\/?(.*)_test\.rb$})
20
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { "test" }
21
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
22
+ end
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # blockly_interpreter
2
+
3
+ An interpreter for [Blockly](https://developers.google.com/blockly/) programs, written in Ruby. blockly_interpreter provides:
4
+
5
+ * Implementations of most (but not all) of the blocks that Blockly ships with
6
+ * The capability to easily implement your own block types
7
+ * Several optional extension blocks to allow accessing Ruby and Rails features
8
+ * A DSL for writing Blockly programs in Ruby
9
+ * Automatic import and export capabilities to translate between that DSL and Blockly XML code
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'blockly_interpreter'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install blockly_interpreter
26
+
27
+ ## Basic Usage
28
+
29
+ To begin with, you'll probably want a global instance of `BlocklyInterpreter::ProgramCache`. This class is thread-safe, so you can use a global instance in multithreaded apps.
30
+
31
+ ```ruby
32
+ my_program_cache = BlocklyInterpreter::ProgramCache.new
33
+ ```
34
+
35
+ Then, load and parse the program, which will return a `BlocklyInterpreter::Program` object.
36
+
37
+ ```ruby
38
+ program = my_program_cache.load_program("<xml>...</xml>")
39
+ ```
40
+
41
+ Finally, spin up a `BlocklyInterpreter::Interpreter` and run your program:
42
+
43
+ ```ruby
44
+ interpreter = BlocklyInterpreter::Interpreter.new
45
+ interpreter.execute(program)
46
+ ```
47
+
48
+ ## Custom Interpreters and Execution Contexts
49
+
50
+ In many situations, you might need to create a custom interpreter subclass. For example:
51
+
52
+ * You might want the Blockly program to produce some particular type of output
53
+ * You might want to provide additional context for the program to use when it runs
54
+ * You might want to create custom blocks for certain types of Blockly programs to use
55
+
56
+ To do this, you'll likely need to create subclasses of two things: `BlocklyInterpreter::ExecutionContext` and `BlocklyInterpreter::Interpreter`. The `BlocklyInterpreter::ExecutionContext` object is passed into all Block classes in their `execute_statement` and `value` methods, so it can be used to expose external objects to custom blocks, or to allow custom blocks to set output values from the program. The `BlocklyInterpreter::Interpreter` object is the external interface that allows programs to be run, so it will need to be configured to use your custom `ExecutionContext` class, to set it up with the right input values, and to expose any output values it might produce.
57
+
58
+ For an example of how this might work, see `TestInterpreter` and `TestInterpreter::ExecutionContext` in `test/test_helper.rb`.
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
63
+
64
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nbudin/blockly_interpreter.
69
+
70
+ ## License
71
+
72
+ blockly_interpreter is Copyright &copy; Nat Budin and is distributed under the terms and conditions of the MIT License. See the COPYING file for details.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1 @@
1
+ //= require_tree './extension_blocks'
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ goog.provide('BlocklyInterpreter.ExtensionBlocks.dates');
4
+ goog.require('Blockly.Blocks');
5
+
6
+ BlocklyInterpreter.ExtensionBlocks.dates.HUE = 280;
7
+
8
+ Blockly.Blocks['date_today'] = {
9
+ init: function() {
10
+ this.appendDummyInput()
11
+ .setAlign(Blockly.ALIGN_RIGHT)
12
+ .appendField('today');
13
+ this.setOutput(true, "Date");
14
+ this.setColour(BlocklyInterpreter.ExtensionBlocks.dates.HUE);
15
+ }
16
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ goog.provide('BlocklyInterpreter.ExtensionBlocks.debugging');
4
+ goog.require('Blockly.Blocks');
5
+
6
+ BlocklyInterpreter.ExtensionBlocks.debugging.HUE = 310;
7
+
8
+ Blockly.Blocks["debug_message"] = {
9
+ init: function() {
10
+ this.appendValueInput("MESSAGE")
11
+ .appendField("log debugging message");
12
+ this.setInputsInline(true);
13
+ this.setPreviousStatement(true);
14
+ this.setNextStatement(true);
15
+ this.setColour(BlocklyInterpreter.ExtensionBlocks.debugging.HUE);
16
+ }
17
+ };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ goog.provide('BlocklyInterpreter.ExtensionBlocks.lists');
4
+ goog.require('Blockly.Blocks');
5
+
6
+ Blockly.Blocks["lists_include"] = {
7
+ init: function() {
8
+ this.appendValueInput("A")
9
+ .setCheck("Array");
10
+ this.appendDummyInput()
11
+ .appendField(new Blockly.FieldDropdown([["includes", "INCLUDE"], ["does not include", "NINCLUDE"]]), "OP");
12
+ this.appendValueInput("B");
13
+ this.setInputsInline(true);
14
+ this.setOutput(true, "Boolean");
15
+ this.setColour(Blockly.Blocks.lists.HUE);
16
+ this.setTooltip('Checks a list to see if a particular value is part of it or not');
17
+ }
18
+ };
19
+
20
+ Blockly.Blocks['lists_append'] = {
21
+ init: function() {
22
+ this.appendValueInput("VALUE")
23
+ .setAlign(Blockly.ALIGN_RIGHT)
24
+ .appendField("append value");
25
+ this.appendValueInput("LIST")
26
+ .setCheck("Array")
27
+ .setAlign(Blockly.ALIGN_RIGHT)
28
+ .appendField("to list");
29
+ this.setPreviousStatement(true);
30
+ this.setNextStatement(true);
31
+ this.setColour(Blockly.Blocks.lists.HUE);
32
+ }
33
+ };
34
+
35
+ Blockly.Blocks['lists_concat'] = {
36
+ init: function() {
37
+ this.appendValueInput("LIST1")
38
+ .setCheck("Array")
39
+ .appendField("list by combining lists");
40
+ this.appendValueInput("LIST2")
41
+ .setCheck("Array");
42
+ this.setOutput(true);
43
+ this.setColour(Blockly.Blocks.lists.HUE);
44
+ }
45
+ };
@@ -0,0 +1,274 @@
1
+ 'use strict';
2
+
3
+ goog.provide('BlocklyInterpreter.ExtensionBlocks.logic');
4
+ goog.require('Blockly.Blocks');
5
+
6
+ Blockly.Blocks["object_present"] = {
7
+ init: function() {
8
+ this.appendValueInput("VALUE");
9
+ this.appendDummyInput()
10
+ .appendField("is present");
11
+ this.setInputsInline(true);
12
+ this.setOutput(true);
13
+ this.setColour(Blockly.Blocks.logic.HUE);
14
+ }
15
+ };
16
+
17
+ Blockly.Blocks['controls_switch'] = {
18
+ /**
19
+ * Block for if/elseif/else condition.
20
+ * @this Blockly.Block
21
+ */
22
+ init: function() {
23
+ this.setColour(Blockly.Blocks.logic.HUE);
24
+ this.appendValueInput('SWITCH_VAL')
25
+ .setAlign(Blockly.ALIGN_RIGHT)
26
+ .appendField("when");
27
+ this.setInputsInline(false);
28
+ this.setPreviousStatement(true);
29
+ this.setNextStatement(true);
30
+ this.setMutator(new Blockly.Mutator(['controls_switch_case',
31
+ 'controls_switch_else']));
32
+ // Assign 'this' to a variable for use in the tooltip closure below.
33
+ var thisBlock = this;
34
+ this.caseCount_ = 0;
35
+ this.elseCount_ = 0;
36
+ this.captureCount_ = 0;
37
+ },
38
+ /**
39
+ * Create XML to represent the number of else-if and else inputs.
40
+ * @return {Element} XML storage element.
41
+ * @this Blockly.Block
42
+ */
43
+ mutationToDom: function() {
44
+ if (!this.caseCount_ && !this.elseCount_) {
45
+ return null;
46
+ }
47
+ var container = document.createElement('mutation');
48
+ if (this.caseCount_) {
49
+ container.setAttribute('case', this.caseCount_);
50
+ }
51
+ if (this.elseCount_) {
52
+ container.setAttribute('else', 1);
53
+ }
54
+ if (this.captureCount_) {
55
+ container.setAttribute('capture', 1);
56
+ }
57
+ return container;
58
+ },
59
+ /**
60
+ * Parse XML to restore the else-if and else inputs.
61
+ * @param {!Element} xmlElement XML storage element.
62
+ * @this Blockly.Block
63
+ */
64
+ domToMutation: function(xmlElement) {
65
+ this.caseCount_ = parseInt(xmlElement.getAttribute('case'), 10) || 0;
66
+ this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0;
67
+ this.captureCount_ = parseInt(xmlElement.getAttribute('capture'), 10) || 0;
68
+ if (this.captureCount_) {
69
+ this.appendDummyInput("CAPTURE_DUMMY")
70
+ .setAlign(Blockly.ALIGN_RIGHT)
71
+ .appendField("which we call")
72
+ .appendField(new Blockly.FieldVariable("captured_value"), "CAPTURE_VAR");
73
+ }
74
+ for (var i = 0; i < this.caseCount_; i++) {
75
+ this.appendValueInput('CASE' + i)
76
+ .setAlign(Blockly.ALIGN_RIGHT)
77
+ .appendField("is equal to");
78
+ this.appendStatementInput('DO' + i)
79
+ .appendField("then");
80
+ }
81
+ if (this.elseCount_) {
82
+ this.appendStatementInput('ELSE')
83
+ .appendField("else");
84
+ }
85
+ },
86
+ /**
87
+ * Populate the mutator's dialog with this block's components.
88
+ * @param {!Blockly.Workspace} workspace Mutator's workspace.
89
+ * @return {!Blockly.Block} Root block in mutator.
90
+ * @this Blockly.Block
91
+ */
92
+ decompose: function(workspace) {
93
+ var containerBlock = Blockly.Block.obtain(workspace, 'controls_switch_switch');
94
+ if (this.captureCount_) {
95
+ containerBlock.setFieldValue('TRUE', 'CAPTURE');
96
+ }
97
+ containerBlock.initSvg();
98
+ var connection = containerBlock.getInput('STACK').connection;
99
+ for (var i = 0; i < this.caseCount_; i++) {
100
+ var caseBlock = Blockly.Block.obtain(workspace, 'controls_switch_case');
101
+ caseBlock.initSvg();
102
+ connection.connect(caseBlock.previousConnection);
103
+ connection = caseBlock.nextConnection;
104
+ }
105
+ if (this.elseCount_) {
106
+ var elseBlock = Blockly.Block.obtain(workspace, 'controls_switch_else');
107
+ elseBlock.initSvg();
108
+ connection.connect(elseBlock.previousConnection);
109
+ }
110
+ return containerBlock;
111
+ },
112
+ /**
113
+ * Reconfigure this block based on the mutator dialog's components.
114
+ * @param {!Blockly.Block} containerBlock Root block in mutator.
115
+ * @this Blockly.Block
116
+ */
117
+ compose: function(containerBlock) {
118
+ // Disconnect the capture block and remove the input.
119
+ if (this.captureCount_) {
120
+ this.removeInput("CAPTURE_DUMMY");
121
+ }
122
+ this.captureCount_ = 0;
123
+ // Disconnect the else input blocks and remove the inputs.
124
+ if (this.elseCount_) {
125
+ this.removeInput('ELSE');
126
+ }
127
+ this.elseCount_ = 0;
128
+ // Disconnect all the elseif input blocks and remove the inputs.
129
+ for (var i = 0; i < this.caseCount_; i++) {
130
+ this.removeInput('CASE' + i);
131
+ this.removeInput('DO' + i);
132
+ }
133
+ this.caseCount_ = 0;
134
+ // Rebuild the block's optional inputs.
135
+ if (containerBlock.getFieldValue('CAPTURE') == "TRUE") {
136
+ this.appendDummyInput("CAPTURE_DUMMY")
137
+ .setAlign(Blockly.ALIGN_RIGHT)
138
+ .appendField("which we call")
139
+ .appendField(new Blockly.FieldVariable(containerBlock.captureVar_ || "captured_value"), "CAPTURE_VAR");
140
+ this.captureCount_++;
141
+ }
142
+ var clauseBlock = containerBlock.getInputTargetBlock('STACK');
143
+ while (clauseBlock) {
144
+ switch (clauseBlock.type) {
145
+ case 'controls_switch_case':
146
+ var caseInput = this.appendValueInput('CASE' + this.caseCount_)
147
+ .setAlign(Blockly.ALIGN_RIGHT)
148
+ .appendField("is equal to");
149
+ var doInput = this.appendStatementInput('DO' + this.caseCount_);
150
+ doInput.appendField("then");
151
+ // Reconnect any child blocks.
152
+ if (clauseBlock.valueConnection_) {
153
+ caseInput.connection.connect(clauseBlock.valueConnection_);
154
+ }
155
+ if (clauseBlock.statementConnection_) {
156
+ doInput.connection.connect(clauseBlock.statementConnection_);
157
+ }
158
+ this.caseCount_++;
159
+ break;
160
+ case 'controls_switch_else':
161
+ var elseInput = this.appendStatementInput('ELSE');
162
+ elseInput.appendField("else");
163
+ // Reconnect any child blocks.
164
+ if (clauseBlock.statementConnection_) {
165
+ elseInput.connection.connect(clauseBlock.statementConnection_);
166
+ }
167
+ this.elseCount_++;
168
+ break;
169
+ default:
170
+ throw 'Unknown block type.';
171
+ }
172
+ clauseBlock = clauseBlock.nextConnection &&
173
+ clauseBlock.nextConnection.targetBlock();
174
+ }
175
+ },
176
+ /**
177
+ * Store pointers to any connected child blocks.
178
+ * @param {!Blockly.Block} containerBlock Root block in mutator.
179
+ * @this Blockly.Block
180
+ */
181
+ saveConnections: function(containerBlock) {
182
+ containerBlock.captureVar_ = this.getFieldValue('CAPTURE_VAR');
183
+
184
+ var clauseBlock = containerBlock.getInputTargetBlock('STACK');
185
+ var i = 0;
186
+ while (clauseBlock) {
187
+ switch (clauseBlock.type) {
188
+ case 'controls_switch_case':
189
+ var inputCase = this.getInput('CASE' + i);
190
+ var inputDo = this.getInput('DO' + i);
191
+ clauseBlock.valueConnection_ =
192
+ inputCase && inputCase.connection.targetConnection;
193
+ clauseBlock.statementConnection_ =
194
+ inputDo && inputDo.connection.targetConnection;
195
+ i++;
196
+ break;
197
+ case 'controls_switch_else':
198
+ var inputDo = this.getInput('ELSE');
199
+ clauseBlock.statementConnection_ =
200
+ inputDo && inputDo.connection.targetConnection;
201
+ break;
202
+ default:
203
+ throw 'Unknown block type.';
204
+ }
205
+ clauseBlock = clauseBlock.nextConnection &&
206
+ clauseBlock.nextConnection.targetBlock();
207
+ }
208
+ },
209
+
210
+ /**
211
+ * Notification that a variable is renaming.
212
+ * If the name matches one of this block's variables, rename it.
213
+ * @param {string} oldName Previous name of variable.
214
+ * @param {string} newName Renamed variable.
215
+ * @this Blockly.Block
216
+ */
217
+ renameVar: function(oldName, newName) {
218
+ var captureVarName = this.getFieldValue('CAPTURE_VAR');
219
+
220
+ if (captureVarName && Blockly.Names.equals(oldName, captureVarName)) {
221
+ this.setFieldValue(newName, 'CAPTURE_VAR');
222
+ }
223
+ },
224
+
225
+ getVars: function() {
226
+ return [this.getFieldValue('CAPTURE_VAR')];
227
+ }
228
+ };
229
+
230
+ Blockly.Blocks['controls_switch_switch'] = {
231
+ /**
232
+ * Mutator block for if container.
233
+ * @this Blockly.Block
234
+ */
235
+ init: function() {
236
+ this.setColour(Blockly.Blocks.logic.HUE);
237
+ this.appendDummyInput()
238
+ .appendField("when value");
239
+ this.appendDummyInput()
240
+ .appendField(new Blockly.FieldCheckbox(false), "CAPTURE")
241
+ .appendField("capture value");
242
+ this.appendStatementInput('STACK');
243
+ this.contextMenu = false;
244
+ }
245
+ };
246
+
247
+ Blockly.Blocks['controls_switch_case'] = {
248
+ /**
249
+ * Mutator bolck for else-if condition.
250
+ * @this Blockly.Block
251
+ */
252
+ init: function() {
253
+ this.setColour(Blockly.Blocks.logic.HUE);
254
+ this.appendDummyInput()
255
+ .appendField("is equal to");
256
+ this.setPreviousStatement(true);
257
+ this.setNextStatement(true);
258
+ this.contextMenu = false;
259
+ }
260
+ };
261
+
262
+ Blockly.Blocks['controls_switch_else'] = {
263
+ /**
264
+ * Mutator block for else condition.
265
+ * @this Blockly.Block
266
+ */
267
+ init: function() {
268
+ this.setColour(Blockly.Blocks.logic.HUE);
269
+ this.appendDummyInput()
270
+ .appendField("else");
271
+ this.setPreviousStatement(true);
272
+ this.contextMenu = false;
273
+ }
274
+ };