blockly_interpreter 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +12 -0
- data/COPYING +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +91 -0
- data/Guardfile +22 -0
- data/README.md +72 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks.js +1 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks/dates.js +16 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks/debugging.js +17 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks/lists.js +45 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks/logic.js +274 -0
- data/app/assets/javascripts/blockly_interpreter/extension_blocks/text.js.erb +21 -0
- data/bin/_guard-core +16 -0
- data/bin/console +14 -0
- data/bin/guard +16 -0
- data/bin/rake +16 -0
- data/bin/setup +7 -0
- data/blockly_interpreter.gemspec +33 -0
- data/exe/blocklyi +13 -0
- data/exe/rublocklyc +31 -0
- data/lib/blockly_interpreter.rb +25 -0
- data/lib/blockly_interpreter/block.rb +113 -0
- data/lib/blockly_interpreter/block_library.rb +14 -0
- data/lib/blockly_interpreter/console_interpreter.rb +15 -0
- data/lib/blockly_interpreter/core_blocks.rb +55 -0
- data/lib/blockly_interpreter/core_blocks/arithmetic_operator_block.rb +24 -0
- data/lib/blockly_interpreter/core_blocks/boolean_block.rb +23 -0
- data/lib/blockly_interpreter/core_blocks/comparison_operator_block.rb +50 -0
- data/lib/blockly_interpreter/core_blocks/for_block.rb +68 -0
- data/lib/blockly_interpreter/core_blocks/for_each_block.rb +42 -0
- data/lib/blockly_interpreter/core_blocks/get_variable_block.rb +19 -0
- data/lib/blockly_interpreter/core_blocks/if_block.rb +105 -0
- data/lib/blockly_interpreter/core_blocks/lists_create_empty_block.rb +17 -0
- data/lib/blockly_interpreter/core_blocks/lists_create_with_block.rb +48 -0
- data/lib/blockly_interpreter/core_blocks/lists_get_index_block.rb +95 -0
- data/lib/blockly_interpreter/core_blocks/logic_negate_block.rb +20 -0
- data/lib/blockly_interpreter/core_blocks/logical_operator_block.rb +22 -0
- data/lib/blockly_interpreter/core_blocks/number_block.rb +23 -0
- data/lib/blockly_interpreter/core_blocks/procedure_block.rb +49 -0
- data/lib/blockly_interpreter/core_blocks/procedures_call_no_return_block.rb +21 -0
- data/lib/blockly_interpreter/core_blocks/procedures_call_return_block.rb +21 -0
- data/lib/blockly_interpreter/core_blocks/procedures_def_no_return_block.rb +31 -0
- data/lib/blockly_interpreter/core_blocks/procedures_def_return_block.rb +64 -0
- data/lib/blockly_interpreter/core_blocks/procedures_if_return_block.rb +45 -0
- data/lib/blockly_interpreter/core_blocks/repeat_times_block.rb +33 -0
- data/lib/blockly_interpreter/core_blocks/set_variable_block.rb +24 -0
- data/lib/blockly_interpreter/core_blocks/text_block.rb +23 -0
- data/lib/blockly_interpreter/core_blocks/text_change_case_block.rb +32 -0
- data/lib/blockly_interpreter/core_blocks/text_join_block.rb +50 -0
- data/lib/blockly_interpreter/dsl.rb +291 -0
- data/lib/blockly_interpreter/dsl_generator.rb +147 -0
- data/lib/blockly_interpreter/engine.rb +8 -0
- data/lib/blockly_interpreter/execution_context.rb +72 -0
- data/lib/blockly_interpreter/extension_blocks.rb +25 -0
- data/lib/blockly_interpreter/extension_blocks/date_today_block.rb +17 -0
- data/lib/blockly_interpreter/extension_blocks/debug_message_block.rb +18 -0
- data/lib/blockly_interpreter/extension_blocks/lists_append_block.rb +34 -0
- data/lib/blockly_interpreter/extension_blocks/lists_concat_block.rb +37 -0
- data/lib/blockly_interpreter/extension_blocks/lists_include_operator_block.rb +52 -0
- data/lib/blockly_interpreter/extension_blocks/object_present_block.rb +21 -0
- data/lib/blockly_interpreter/extension_blocks/switch_block.rb +107 -0
- data/lib/blockly_interpreter/extension_blocks/text_inflect_block.rb +27 -0
- data/lib/blockly_interpreter/generic_block_dsl_generator.rb +64 -0
- data/lib/blockly_interpreter/interpreter.rb +25 -0
- data/lib/blockly_interpreter/parser.rb +117 -0
- data/lib/blockly_interpreter/program.rb +51 -0
- data/lib/blockly_interpreter/program_cache.rb +19 -0
- data/lib/blockly_interpreter/test_helper.rb +98 -0
- data/lib/blockly_interpreter/version.rb +3 -0
- 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
data/.travis.yml
ADDED
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
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 © 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 @@
|
|
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
|
+
};
|