hotcell 0.0.1
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.
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +15 -0
- data/Guardfile +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +17 -0
- data/hotcell.gemspec +22 -0
- data/lib/hotcell/.DS_Store +0 -0
- data/lib/hotcell/config.rb +31 -0
- data/lib/hotcell/context.rb +36 -0
- data/lib/hotcell/errors.rb +43 -0
- data/lib/hotcell/extensions.rb +42 -0
- data/lib/hotcell/lexer.rb +783 -0
- data/lib/hotcell/lexer.rl +299 -0
- data/lib/hotcell/manipulator.rb +31 -0
- data/lib/hotcell/node/arrayer.rb +7 -0
- data/lib/hotcell/node/assigner.rb +11 -0
- data/lib/hotcell/node/block.rb +58 -0
- data/lib/hotcell/node/calculator.rb +35 -0
- data/lib/hotcell/node/command.rb +41 -0
- data/lib/hotcell/node/hasher.rb +7 -0
- data/lib/hotcell/node/joiner.rb +7 -0
- data/lib/hotcell/node/sequencer.rb +7 -0
- data/lib/hotcell/node/summoner.rb +11 -0
- data/lib/hotcell/node/tag.rb +26 -0
- data/lib/hotcell/node.rb +55 -0
- data/lib/hotcell/parser.rb +1186 -0
- data/lib/hotcell/parser.y +231 -0
- data/lib/hotcell/scope.rb +57 -0
- data/lib/hotcell/template.rb +29 -0
- data/lib/hotcell/version.rb +3 -0
- data/lib/hotcell.rb +19 -0
- data/misc/rage.rl +1999 -0
- data/misc/unicode2ragel.rb +305 -0
- data/spec/data/dstrings +8 -0
- data/spec/data/sstrings +6 -0
- data/spec/lib/hotcell/config_spec.rb +57 -0
- data/spec/lib/hotcell/context_spec.rb +53 -0
- data/spec/lib/hotcell/lexer_spec.rb +340 -0
- data/spec/lib/hotcell/manipulator_spec.rb +64 -0
- data/spec/lib/hotcell/node/block_spec.rb +188 -0
- data/spec/lib/hotcell/node/command_spec.rb +71 -0
- data/spec/lib/hotcell/parser_spec.rb +382 -0
- data/spec/lib/hotcell/scope_spec.rb +160 -0
- data/spec/lib/hotcell/template_spec.rb +41 -0
- data/spec/lib/hotcell_spec.rb +8 -0
- data/spec/spec_helper.rb +44 -0
- metadata +139 -0
@@ -0,0 +1,299 @@
|
|
1
|
+
%%{
|
2
|
+
#%
|
3
|
+
machine puffer_lexer;
|
4
|
+
|
5
|
+
variable data @data;
|
6
|
+
variable te @te;
|
7
|
+
variable ts @ts;
|
8
|
+
|
9
|
+
plus = '+';
|
10
|
+
minus = '-';
|
11
|
+
multiply = '*';
|
12
|
+
power = '**';
|
13
|
+
divide = '/';
|
14
|
+
modulo = '%';
|
15
|
+
arithmetic = plus | minus | multiply | power | divide | modulo;
|
16
|
+
|
17
|
+
and = '&&';
|
18
|
+
or = '||';
|
19
|
+
not = '!';
|
20
|
+
equal = '==';
|
21
|
+
inequal = '!=';
|
22
|
+
gt = '>';
|
23
|
+
gte = '>=';
|
24
|
+
lt = '<';
|
25
|
+
lte = '<=';
|
26
|
+
logic = and | or | not | equal | inequal | gt | gte | lt | lte;
|
27
|
+
|
28
|
+
assign = '=';
|
29
|
+
comma = ',';
|
30
|
+
period = '.';
|
31
|
+
colon = ':';
|
32
|
+
question = '?';
|
33
|
+
semicolon = ';';
|
34
|
+
newline = '\n';
|
35
|
+
flow = assign | comma | period | colon | question | semicolon | newline;
|
36
|
+
|
37
|
+
array_open = '[';
|
38
|
+
array_close = ']';
|
39
|
+
hash_open = '{';
|
40
|
+
hash_close = '}';
|
41
|
+
bracket_open = '(';
|
42
|
+
bracket_close = ')';
|
43
|
+
structure = array_open | array_close | hash_open | hash_close | bracket_open | bracket_close;
|
44
|
+
|
45
|
+
|
46
|
+
escaped_symbol = '\\' any;
|
47
|
+
|
48
|
+
squote = "'";
|
49
|
+
snon_quote = [^\\'];
|
50
|
+
sstring = squote (snon_quote | escaped_symbol)* squote @lerr{ raise_unterminated_string };
|
51
|
+
|
52
|
+
dquote = '"';
|
53
|
+
dnon_quote = [^\\"];
|
54
|
+
dstring = dquote (dnon_quote | escaped_symbol)* dquote @lerr{ raise_unterminated_string };
|
55
|
+
|
56
|
+
rquote = '/';
|
57
|
+
rnon_quote = [^\\/];
|
58
|
+
regexp = rquote @{ regexp_ambiguity { fgoto expression; } }
|
59
|
+
(rnon_quote | escaped_symbol)* rquote alpha* @lerr{ raise_unterminated_regexp };
|
60
|
+
|
61
|
+
|
62
|
+
numeric = digit* ('.' digit+)?;
|
63
|
+
identifer = (alpha | '_') (alnum | '_')* [?!]?;
|
64
|
+
operator = arithmetic | logic | flow | structure;
|
65
|
+
comment = '#' ([^\n}]+ | '}' [^}])*;
|
66
|
+
blank = [\t\v\f\r ];
|
67
|
+
|
68
|
+
tag_open = '{{' [!\#]?;
|
69
|
+
tag_close = '}}';
|
70
|
+
template = [^{]+ | '{';
|
71
|
+
|
72
|
+
template_comment_close = '#}}';
|
73
|
+
template_comment_body = [^\#]+ | '#';
|
74
|
+
|
75
|
+
|
76
|
+
expression := |*
|
77
|
+
tag_close => { emit_tag; fret; };
|
78
|
+
operator => { emit_operator };
|
79
|
+
numeric => { emit_numeric };
|
80
|
+
identifer => { emit_identifer };
|
81
|
+
sstring => { emit_sstring };
|
82
|
+
dstring => { emit_dstring };
|
83
|
+
regexp => { emit_regexp };
|
84
|
+
comment => { emit_comment };
|
85
|
+
blank;
|
86
|
+
*|;
|
87
|
+
|
88
|
+
template_comment := |*
|
89
|
+
template_comment_close => { emit_comment; fret; };
|
90
|
+
template_comment_body => { emit_comment };
|
91
|
+
*|;
|
92
|
+
|
93
|
+
main := |*
|
94
|
+
tag_open => { emit_tag_or_comment ->{ fcall expression; }, ->{ fcall template_comment; } };
|
95
|
+
template => { emit_template };
|
96
|
+
*|;
|
97
|
+
}%%
|
98
|
+
#%
|
99
|
+
|
100
|
+
module Hotcell
|
101
|
+
class Lexer
|
102
|
+
OPERATIONS = {
|
103
|
+
'+' => :PLUS, '-' => :MINUS, '*' => :MULTIPLY, '**' => :POWER, '/' => :DIVIDE, '%' => :MODULO,
|
104
|
+
|
105
|
+
'&&' => :AND, '||' => :OR, '!' => :NOT, '==' => :EQUAL, '!=' => :INEQUAL,
|
106
|
+
'>' => :GT, '>=' => :GTE, '<' => :LT, '<=' => :LTE,
|
107
|
+
|
108
|
+
'=' => :ASSIGN, ',' => :COMMA, '.' => :PERIOD, ':' => :COLON, '?' => :QUESTION,
|
109
|
+
';' => :SEMICOLON
|
110
|
+
}
|
111
|
+
|
112
|
+
BOPEN = { '[' => :AOPEN, '{' => :HOPEN, '(' => :POPEN }
|
113
|
+
BCLOSE = { ']' => :ACLOSE, '}' => :HCLOSE, ')' => :PCLOSE }
|
114
|
+
BRACKETS = BOPEN.merge(BCLOSE)
|
115
|
+
|
116
|
+
OPERATORS = OPERATIONS.merge(BRACKETS).merge("\n" => :NEWLINE)
|
117
|
+
|
118
|
+
CONSTANTS = {
|
119
|
+
'nil' => [:NIL, nil], 'null' => [:NIL, nil],
|
120
|
+
'false' => [:FALSE, false], 'true' => [:TRUE, true]
|
121
|
+
}
|
122
|
+
|
123
|
+
SSTRING_ESCAPE_REGEXP = /\\\'|\\\\/
|
124
|
+
SSTRING_ESCAPE_MAP = { "\\'" => "'", "\\\\" => "\\" }
|
125
|
+
|
126
|
+
DSTRING_ESCAPE_REGEXP = /\\./
|
127
|
+
DSTRING_ESCAPE_MAP = {
|
128
|
+
'\\"' => '"', "\\\\" => "\\", '\n' => "\n",
|
129
|
+
'\s' => "\s", '\r' => "\r", '\t' => "\t"
|
130
|
+
}
|
131
|
+
|
132
|
+
TAGS = {
|
133
|
+
'{{' => :TOPEN, '{{!' => :TOPEN,
|
134
|
+
'}}' => :TCLOSE
|
135
|
+
}
|
136
|
+
|
137
|
+
PREREGEXP = Set.new [
|
138
|
+
:TOPEN, :NEWLINE, :SEMICOLON,
|
139
|
+
:COLON, :COMMA, :PERIOD,
|
140
|
+
:POPEN, :AOPEN, :HOPEN
|
141
|
+
]
|
142
|
+
|
143
|
+
def initialize source
|
144
|
+
@source = source
|
145
|
+
@data = @source.unpack 'c*'
|
146
|
+
|
147
|
+
%% write data;
|
148
|
+
#%
|
149
|
+
end
|
150
|
+
|
151
|
+
def emit symbol, value
|
152
|
+
@token_array << [symbol, value]
|
153
|
+
end
|
154
|
+
|
155
|
+
def current_value
|
156
|
+
@data[@ts...@te].pack('c*').force_encoding('UTF-8')
|
157
|
+
end
|
158
|
+
|
159
|
+
def emit_operator
|
160
|
+
value = current_value
|
161
|
+
emit OPERATORS[value], value
|
162
|
+
end
|
163
|
+
|
164
|
+
def emit_numeric
|
165
|
+
# last = @token_array[-1]
|
166
|
+
# pre_last = @token_array[-2]
|
167
|
+
# # This need to give unary minus with numeric higher precedence then unari minus with
|
168
|
+
# last[0] = :NEGATIVE if last && last[0] == :MINUS &&
|
169
|
+
# (!pre_last || pre_last[0].in?())
|
170
|
+
|
171
|
+
value = current_value
|
172
|
+
if value =~ /\./
|
173
|
+
emit :FLOAT, Float(value)
|
174
|
+
else
|
175
|
+
emit :INTEGER, Integer(value)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def emit_identifer
|
180
|
+
value = current_value
|
181
|
+
if args = CONSTANTS[value]
|
182
|
+
emit *args
|
183
|
+
else
|
184
|
+
emit :IDENTIFER, value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def emit_sstring
|
189
|
+
emit :STRING, current_value[1..-2].gsub(SSTRING_ESCAPE_REGEXP) { |match|
|
190
|
+
SSTRING_ESCAPE_MAP[match] }.force_encoding('UTF-8')
|
191
|
+
end
|
192
|
+
|
193
|
+
def emit_dstring
|
194
|
+
emit :STRING, current_value[1..-2].gsub(DSTRING_ESCAPE_REGEXP) { |match|
|
195
|
+
DSTRING_ESCAPE_MAP[match] || match[1] }
|
196
|
+
end
|
197
|
+
|
198
|
+
def regexp_ambiguity
|
199
|
+
unless regexp_possible?
|
200
|
+
emit_operator
|
201
|
+
yield
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def regexp_possible?
|
206
|
+
last = @token_array[-1]
|
207
|
+
# Need more rules!
|
208
|
+
!last || PREREGEXP.include?(last[0])
|
209
|
+
end
|
210
|
+
|
211
|
+
def emit_regexp
|
212
|
+
value = current_value
|
213
|
+
finish = value.rindex('/')
|
214
|
+
|
215
|
+
options_string = value[finish+1..-1]
|
216
|
+
options = 0
|
217
|
+
options |= Regexp::EXTENDED if options_string.include?('x')
|
218
|
+
options |= Regexp::IGNORECASE if options_string.include?('i')
|
219
|
+
options |= Regexp::MULTILINE if options_string.include?('m')
|
220
|
+
|
221
|
+
emit :REGEXP, Regexp.new(value[1..finish-1], options)
|
222
|
+
end
|
223
|
+
|
224
|
+
def emit_template
|
225
|
+
# Hack this to glue templates going straight
|
226
|
+
last = @token_array[-1]
|
227
|
+
if last && last[0] == :TEMPLATE
|
228
|
+
last[1] += current_value
|
229
|
+
else
|
230
|
+
emit :TEMPLATE, current_value
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def emit_tag_or_comment if_tag, if_comment
|
235
|
+
value = current_value
|
236
|
+
if value == '{{#'
|
237
|
+
emit_comment
|
238
|
+
if_comment.call
|
239
|
+
else
|
240
|
+
emit_tag
|
241
|
+
if_tag.call
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def emit_tag
|
246
|
+
value = current_value
|
247
|
+
emit TAGS[value], value
|
248
|
+
end
|
249
|
+
|
250
|
+
def emit_comment
|
251
|
+
last = @token_array[-1]
|
252
|
+
if last && last[0] == :COMMENT
|
253
|
+
last[1] += current_value
|
254
|
+
else
|
255
|
+
emit :COMMENT, current_value
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def current_position
|
260
|
+
parsed = @data[0..@ts].pack('c*').force_encoding('UTF-8')
|
261
|
+
line = parsed.count("\n") + 1
|
262
|
+
column = parsed.size - 1 - (parsed.rindex("\n") || -1)
|
263
|
+
[line, column]
|
264
|
+
end
|
265
|
+
|
266
|
+
def raise_unexpected_symbol
|
267
|
+
raise Hotcell::Errors::UnexpectedSymbol.new *current_position
|
268
|
+
end
|
269
|
+
|
270
|
+
def raise_unterminated_string
|
271
|
+
raise Hotcell::Errors::UnterminatedString.new *current_position
|
272
|
+
end
|
273
|
+
|
274
|
+
def raise_unterminated_regexp
|
275
|
+
raise Hotcell::Errors::UnterminatedRegexp.new *current_position
|
276
|
+
end
|
277
|
+
|
278
|
+
def tokens
|
279
|
+
@tokens ||= tokenize
|
280
|
+
end
|
281
|
+
|
282
|
+
def tokenize
|
283
|
+
@token_array = []
|
284
|
+
|
285
|
+
%% write init;
|
286
|
+
#%
|
287
|
+
|
288
|
+
eof = pe
|
289
|
+
stack = []
|
290
|
+
|
291
|
+
%% write exec;
|
292
|
+
#%
|
293
|
+
|
294
|
+
raise_unexpected_symbol unless @ts.nil?
|
295
|
+
|
296
|
+
@token_array
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Manipulator
|
3
|
+
module Mixin
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :manipulator_methods, instance_writter: false
|
8
|
+
self.manipulator_methods = Set.new
|
9
|
+
|
10
|
+
def self.manipulator *methods
|
11
|
+
self.manipulator_methods = Set.new(manipulator_methods.to_a + methods.flatten.map(&:to_s))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_manipulator
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def manipulator_invoke method, *arguments
|
20
|
+
send(method, *arguments) if manipulator_methods.include? method
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
include Mixin
|
25
|
+
|
26
|
+
def manipulator_methods
|
27
|
+
@manipulator_methods ||= Set.new((self.class.public_instance_methods -
|
28
|
+
Hotcell::Manipulator.public_instance_methods).map(&:to_s))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Block < Hotcell::Command
|
3
|
+
class_attribute :_subcommands, instance_writter: false
|
4
|
+
self._subcommands = []
|
5
|
+
|
6
|
+
def self.subcommands *names
|
7
|
+
if names.any?
|
8
|
+
self._subcommands = _subcommands + names.flatten.map(&:to_s)
|
9
|
+
else
|
10
|
+
_subcommands
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def optimize
|
15
|
+
if klass = Hotcell.blocks[name]
|
16
|
+
klass.new name, *children, options
|
17
|
+
else
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def subnodes
|
23
|
+
options[:subnodes] || []
|
24
|
+
end
|
25
|
+
|
26
|
+
def subcommands
|
27
|
+
subnodes.select { |node| node.is_a?(Hash) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
valid = subcommands.map { |subcommand| subcommand[:name] } - _subcommands == []
|
32
|
+
raise Hotcell::Errors::BlockError.new 'Invalid block syntax' unless valid
|
33
|
+
end
|
34
|
+
|
35
|
+
def process context, subnodes, *args
|
36
|
+
end
|
37
|
+
|
38
|
+
def render context
|
39
|
+
context.safe do
|
40
|
+
subnodes = render_subnodes(context)
|
41
|
+
values = render_nodes(context, children)
|
42
|
+
concat context, process(context, subnodes, *values)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def render_subnodes context
|
47
|
+
subnodes.map do |node|
|
48
|
+
if node.is_a?(Hash)
|
49
|
+
subcommand = { name: node[:name] }
|
50
|
+
subcommand.merge!(args: node[:args].render(context)) if node[:args]
|
51
|
+
subcommand
|
52
|
+
else
|
53
|
+
node.render(context)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Calculator < Hotcell::Node
|
3
|
+
HANDLERS = {
|
4
|
+
:PLUS => ->(value1, value2) { value1 + value2 },
|
5
|
+
:MINUS => ->(value1, value2) { value1 - value2 },
|
6
|
+
:UMINUS => ->(value) { -value },
|
7
|
+
:UPLUS => ->(value) { value },
|
8
|
+
:MULTIPLY => ->(value1, value2) { value1 * value2 },
|
9
|
+
:POWER => ->(value1, value2) { value1 ** value2 },
|
10
|
+
:DIVIDE => ->(value1, value2) { value1 / value2 },
|
11
|
+
:MODULO => ->(value1, value2) { value1 % value2 },
|
12
|
+
:AND => ->(value1, value2) { value1 && value2 },
|
13
|
+
:OR => ->(value1, value2) { value1 || value2 },
|
14
|
+
:NOT => ->(value) { !value },
|
15
|
+
:EQUAL => ->(value1, value2) { value1 == value2 },
|
16
|
+
:INEQUAL => ->(value1, value2) { value1 != value2 },
|
17
|
+
:GT => ->(value1, value2) { value1 > value2 },
|
18
|
+
:GTE => ->(value1, value2) { value1 >= value2 },
|
19
|
+
:LT => ->(value1, value2) { value1 < value2 },
|
20
|
+
:LTE => ->(value1, value2) { value1 <= value2 }
|
21
|
+
}
|
22
|
+
|
23
|
+
def optimize
|
24
|
+
handler = HANDLERS[name]
|
25
|
+
reducible = handler && children.none? { |child| child.is_a? Hotcell::Node }
|
26
|
+
reducible ? handler.call(*children) : super
|
27
|
+
end
|
28
|
+
|
29
|
+
def process context, *values
|
30
|
+
handler = HANDLERS[name]
|
31
|
+
raise "Could not find handler for `#{name}`" unless handler
|
32
|
+
handler.call *values
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Command < Hotcell::Node
|
3
|
+
attr_reader :mode, :assign
|
4
|
+
|
5
|
+
def initialize *_
|
6
|
+
super
|
7
|
+
@mode = options[:mode] || :normal
|
8
|
+
@assign = options[:assign]
|
9
|
+
end
|
10
|
+
|
11
|
+
def optimize
|
12
|
+
if klass = Hotcell.commands[name]
|
13
|
+
klass.new name, *children, options
|
14
|
+
else
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate!
|
20
|
+
end
|
21
|
+
|
22
|
+
def process context, *args
|
23
|
+
end
|
24
|
+
|
25
|
+
def render context
|
26
|
+
context.safe do
|
27
|
+
concat context, process(context, *render_nodes(context, children))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def concat context, result
|
32
|
+
context[assign] = result if assign
|
33
|
+
case mode
|
34
|
+
when :normal
|
35
|
+
result
|
36
|
+
else
|
37
|
+
''
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Tag < Hotcell::Node
|
3
|
+
attr_reader :mode
|
4
|
+
|
5
|
+
def initialize *_
|
6
|
+
super
|
7
|
+
@mode = options[:mode] || :normal
|
8
|
+
end
|
9
|
+
|
10
|
+
def process context, *values
|
11
|
+
values.last
|
12
|
+
end
|
13
|
+
|
14
|
+
def render context
|
15
|
+
context.safe do
|
16
|
+
values = render_nodes(context, children)
|
17
|
+
case mode
|
18
|
+
when :normal
|
19
|
+
process context, *values
|
20
|
+
else
|
21
|
+
''
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/hotcell/node.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Hotcell
|
2
|
+
class Node
|
3
|
+
attr_accessor :name, :children, :options
|
4
|
+
|
5
|
+
def self.build *args
|
6
|
+
new(*args).optimize
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize name, *args
|
10
|
+
@name = name
|
11
|
+
@options = args.extract_options!
|
12
|
+
@children = args
|
13
|
+
end
|
14
|
+
|
15
|
+
def optimize
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def [] key
|
20
|
+
children[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
def render context
|
24
|
+
process context, *render_nodes(context, children)
|
25
|
+
end
|
26
|
+
|
27
|
+
def process context, *values
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_nodes context, *values
|
32
|
+
values.flatten.map do |node|
|
33
|
+
node.is_a?(Hotcell::Node) ? node.render(context) : node
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def == other
|
38
|
+
other.is_a?(self.class) &&
|
39
|
+
name == other.name &&
|
40
|
+
options == other.options &&
|
41
|
+
children == other.children
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
require 'hotcell/node/calculator'
|
47
|
+
require 'hotcell/node/assigner'
|
48
|
+
require 'hotcell/node/summoner'
|
49
|
+
require 'hotcell/node/arrayer'
|
50
|
+
require 'hotcell/node/hasher'
|
51
|
+
require 'hotcell/node/sequencer'
|
52
|
+
require 'hotcell/node/joiner'
|
53
|
+
require 'hotcell/node/tag'
|
54
|
+
require 'hotcell/node/command'
|
55
|
+
require 'hotcell/node/block'
|