opal 0.5.2 → 0.5.4
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/lib/opal.rb +0 -5
- data/lib/opal/compiler.rb +24 -44
- data/lib/opal/nodes/base.rb +5 -8
- data/lib/opal/nodes/call.rb +4 -0
- data/lib/opal/nodes/class.rb +6 -7
- data/lib/opal/nodes/def.rb +4 -4
- data/lib/opal/nodes/definitions.rb +0 -14
- data/lib/opal/nodes/iter.rb +51 -38
- data/lib/opal/nodes/literal.rb +21 -24
- data/lib/opal/nodes/module.rb +4 -4
- data/lib/opal/nodes/runtime_helpers.rb +45 -0
- data/lib/opal/nodes/scope.rb +280 -0
- data/lib/opal/nodes/singleton_class.rb +4 -5
- data/lib/opal/nodes/super.rb +1 -1
- data/lib/opal/nodes/top.rb +9 -7
- data/lib/opal/nodes/yield.rb +14 -3
- data/lib/opal/parser.rb +4 -18
- data/lib/opal/parser/grammar.rb +3745 -3667
- data/lib/opal/parser/grammar.y +1692 -1778
- data/lib/opal/parser/keywords.rb +35 -35
- data/lib/opal/parser/lexer.rb +356 -325
- data/lib/opal/parser/sexp.rb +1 -1
- data/lib/opal/version.rb +1 -1
- data/opal.gemspec +1 -0
- data/opal/core/array.rb +320 -81
- data/opal/core/enumerable.rb +46 -5
- data/opal/core/hash.rb +6 -64
- data/opal/core/helpers.rb +67 -0
- data/opal/core/method.rb +1 -1
- data/opal/core/module.rb +4 -4
- data/opal/core/range.rb +1 -12
- data/opal/core/regexp.rb +2 -8
- data/opal/core/runtime.js +74 -3
- data/opal/core/string.rb +99 -74
- data/opal/opal.rb +3 -72
- data/spec/filters/bugs/array.rb +2 -30
- data/spec/filters/bugs/basic_object.rb +0 -1
- data/spec/filters/bugs/string.rb +26 -21
- data/spec/filters/unsupported/enumerator.rb +3 -0
- data/spec/filters/unsupported/float.rb +1 -0
- data/spec/filters/unsupported/immutable_strings.rb +15 -0
- data/spec/filters/unsupported/tainted.rb +58 -30
- data/spec/filters/unsupported/trusted.rb +35 -15
- data/spec/opal/parser/class_spec.rb +4 -4
- data/spec/opal/parser/def_spec.rb +4 -4
- data/spec/opal/parser/lvar_spec.rb +6 -6
- data/spec/opal/parser/module_spec.rb +4 -4
- data/spec/opal/parser/sclass_spec.rb +2 -2
- data/spec/stdlib/native/exposure_spec.rb +33 -0
- data/stdlib/buffer.rb +1 -1
- data/stdlib/buffer/view.rb +1 -1
- data/stdlib/native.rb +193 -174
- data/stdlib/opal-parser.rb +0 -6
- data/stdlib/pp.rb +9 -0
- data/tasks/mspec.rake +3 -1
- metadata +9 -9
- data/lib/opal/nodes/base_scope.rb +0 -11
- data/lib/opal/target_scope.rb +0 -281
- data/spec/filters/20.rb +0 -4
- data/spec/filters/unsupported/array_subclasses.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77632aecb99342d5830be4046997f6f1c4b57c5a
|
4
|
+
data.tar.gz: d0b98b8c4bb8a0cd0e047e45507c4edabdf5a504
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 767a17fdba3738a6dadf6d3d567a5b695ec69bf540d70278af7b0ce1c8a8a529e4c90d1a2f1518486637857dfe8decc1c41ced22d7d99271c4a2b7f727be6db3
|
7
|
+
data.tar.gz: 1b17efbff47540101001edb95dc5190bcbf39218a47f4f8f983a4698c3dec4958e1149ba6ddb3e781b2131071dd8f42a04f0b41768ba7ff067c4079ef8c1b315
|
data/README.md
CHANGED
data/lib/opal.rb
CHANGED
@@ -6,11 +6,6 @@ require 'opal/version'
|
|
6
6
|
# Opal is a ruby to javascript compiler, with a runtime for running
|
7
7
|
# in any javascript environment.
|
8
8
|
module Opal
|
9
|
-
|
10
|
-
def self.compile(source, options = {})
|
11
|
-
Compiler.new.compile(source, options)
|
12
|
-
end
|
13
|
-
|
14
9
|
def self.gem_dir
|
15
10
|
File.expand_path('..', __FILE__.untaint)
|
16
11
|
end
|
data/lib/opal/compiler.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'opal/parser'
|
3
|
-
require 'opal/target_scope'
|
4
|
-
require 'opal/version'
|
5
3
|
require 'opal/fragment'
|
6
4
|
require 'opal/nodes'
|
7
5
|
|
8
6
|
module Opal
|
7
|
+
def self.compile(source, options = {})
|
8
|
+
Compiler.new.compile(source, options)
|
9
|
+
end
|
10
|
+
|
9
11
|
class Compiler
|
10
12
|
# Generated code gets indented with two spaces on each scope
|
11
13
|
INDENT = ' '
|
@@ -15,10 +17,8 @@ module Opal
|
|
15
17
|
COMPARE = %w[< > <= >=]
|
16
18
|
|
17
19
|
# defines a compiler option, also creating method of form 'name?'
|
18
|
-
def self.compiler_option(name, default_value)
|
19
|
-
mid
|
20
|
-
|
21
|
-
define_method(mid) do
|
20
|
+
def self.compiler_option(name, default_value, mid = nil)
|
21
|
+
define_method(mid || name) do
|
22
22
|
@options.fetch(name) { default_value }
|
23
23
|
end
|
24
24
|
end
|
@@ -27,16 +27,16 @@ module Opal
|
|
27
27
|
compiler_option :file, '(file)'
|
28
28
|
|
29
29
|
# adds method stubs for all used methods in file
|
30
|
-
compiler_option :method_missing, true
|
30
|
+
compiler_option :method_missing, true, :method_missing?
|
31
31
|
|
32
32
|
# adds an arity check to every method definition
|
33
|
-
compiler_option :arity_check, false
|
33
|
+
compiler_option :arity_check, false, :arity_check?
|
34
34
|
|
35
35
|
# checks every constant access, delagating to const_missing if needed
|
36
|
-
compiler_option :const_missing, false
|
36
|
+
compiler_option :const_missing, false, :const_missing?
|
37
37
|
|
38
38
|
# compile top level local vars with support for irb style vars
|
39
|
-
compiler_option :irb, false
|
39
|
+
compiler_option :irb, false, :irb?
|
40
40
|
|
41
41
|
# how to handle dynamic requires (:error, :warning, :ignore)
|
42
42
|
compiler_option :dynamic_require_severity, :error
|
@@ -44,13 +44,7 @@ module Opal
|
|
44
44
|
attr_reader :result, :fragments
|
45
45
|
|
46
46
|
# Current scope
|
47
|
-
|
48
|
-
|
49
|
-
# Any helpers required by this file
|
50
|
-
attr_reader :helpers
|
51
|
-
|
52
|
-
# Method calls made in this file
|
53
|
-
attr_reader :method_calls
|
47
|
+
attr_accessor :scope
|
54
48
|
|
55
49
|
# Current case_stmt
|
56
50
|
attr_reader :case_stmt
|
@@ -60,9 +54,6 @@ module Opal
|
|
60
54
|
@indent = ''
|
61
55
|
@unique = 0
|
62
56
|
@options = {}
|
63
|
-
|
64
|
-
@method_calls = Set.new
|
65
|
-
@helpers = Set.new([:breaker, :slice])
|
66
57
|
end
|
67
58
|
|
68
59
|
# Compile some ruby code to a string.
|
@@ -70,10 +61,9 @@ module Opal
|
|
70
61
|
@source = source
|
71
62
|
@options.update options
|
72
63
|
|
73
|
-
@sexp = Parser.new.parse(@source, self.file)
|
64
|
+
@sexp = s(:top, Parser.new.parse(@source, self.file) || s(:nil))
|
74
65
|
|
75
|
-
|
76
|
-
@fragments = top_node.compile_to_fragments.flatten
|
66
|
+
@fragments = process(@sexp).flatten
|
77
67
|
|
78
68
|
@result = @fragments.map(&:code).join('')
|
79
69
|
end
|
@@ -82,6 +72,16 @@ module Opal
|
|
82
72
|
Opal::SourceMap.new(@fragments, source_file || self.file)
|
83
73
|
end
|
84
74
|
|
75
|
+
# Any helpers required by this file
|
76
|
+
def helpers
|
77
|
+
@helpers ||= Set.new([:breaker, :slice])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Method calls made in this file
|
81
|
+
def method_calls
|
82
|
+
@method_calls ||= Set.new
|
83
|
+
end
|
84
|
+
|
85
85
|
# This is called when a parsing/processing error occurs. This
|
86
86
|
# method simply appends the filename and curent line number onto
|
87
87
|
# the message and raises it.
|
@@ -124,27 +124,7 @@ module Opal
|
|
124
124
|
|
125
125
|
# Use the given helper
|
126
126
|
def helper(name)
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
|
-
# Every time the parser enters a new scope, this is called with
|
131
|
-
# the scope type as an argument. Valid types are `:top` for the
|
132
|
-
# top level/file scope; `:class`, `:module` and `:sclass` for the
|
133
|
-
# obvious ruby classes/modules; `:def` and `:iter` for methods
|
134
|
-
# and blocks respectively.
|
135
|
-
#
|
136
|
-
# This method just pushes a new instance of `Opal::Scope` onto the
|
137
|
-
# stack, sets the new scope as the `@scope` variable, and yields
|
138
|
-
# the given block. Once the block returns, the old scope is put
|
139
|
-
# back on top of the stack.
|
140
|
-
def in_scope(type)
|
141
|
-
return unless block_given?
|
142
|
-
|
143
|
-
parent = @scope
|
144
|
-
@scope = TargetScope.new(type, self).tap { |s| s.parent = parent }
|
145
|
-
yield @scope
|
146
|
-
|
147
|
-
@scope = parent
|
127
|
+
self.helpers << name
|
148
128
|
end
|
149
129
|
|
150
130
|
# To keep code blocks nicely indented, this will yield a block after
|
data/lib/opal/nodes/base.rb
CHANGED
@@ -23,18 +23,15 @@ module Opal
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
attr_reader :compiler
|
26
|
+
attr_reader :compiler, :type
|
27
27
|
|
28
28
|
def initialize(sexp, level, compiler)
|
29
29
|
@sexp = sexp
|
30
|
+
@type = sexp.type
|
30
31
|
@level = level
|
31
32
|
@compiler = compiler
|
32
33
|
end
|
33
34
|
|
34
|
-
def type
|
35
|
-
@sexp.type
|
36
|
-
end
|
37
|
-
|
38
35
|
def children
|
39
36
|
@sexp[1..-1]
|
40
37
|
end
|
@@ -119,15 +116,15 @@ module Opal
|
|
119
116
|
end
|
120
117
|
|
121
118
|
def add_local(name)
|
122
|
-
scope.
|
119
|
+
scope.add_scope_local name.to_sym
|
123
120
|
end
|
124
121
|
|
125
122
|
def add_ivar(name)
|
126
|
-
scope.
|
123
|
+
scope.add_scope_ivar name
|
127
124
|
end
|
128
125
|
|
129
126
|
def add_temp(temp)
|
130
|
-
scope.
|
127
|
+
scope.add_scope_temp temp
|
131
128
|
end
|
132
129
|
|
133
130
|
def helper(name)
|
data/lib/opal/nodes/call.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'opal/nodes/base'
|
3
|
+
require 'opal/nodes/runtime_helpers'
|
3
4
|
|
4
5
|
module Opal
|
5
6
|
module Nodes
|
@@ -99,6 +100,9 @@ module Opal
|
|
99
100
|
push result
|
100
101
|
return true
|
101
102
|
end
|
103
|
+
elsif RuntimeHelpers.compatible?(recvr, meth, arglist)
|
104
|
+
push(RuntimeHelpers.new(@sexp, @level, @compiler).compile)
|
105
|
+
return true
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
data/lib/opal/nodes/class.rb
CHANGED
@@ -12,13 +12,13 @@ module Opal
|
|
12
12
|
helper :klass
|
13
13
|
|
14
14
|
push "(function($base, $super) {"
|
15
|
-
line " function
|
16
|
-
line " var self =
|
15
|
+
line " function $#{name}(){};"
|
16
|
+
line " var self = $#{name} = $klass($base, $super, '#{name}', $#{name});"
|
17
17
|
|
18
|
-
in_scope
|
18
|
+
in_scope do
|
19
19
|
scope.name = name
|
20
|
-
add_temp "#{scope.proto} =
|
21
|
-
add_temp "$scope =
|
20
|
+
add_temp "#{scope.proto} = $#{name}._proto"
|
21
|
+
add_temp "$scope = $#{name}._scope"
|
22
22
|
|
23
23
|
body_code = self.body_code
|
24
24
|
empty_line
|
@@ -35,8 +35,7 @@ module Opal
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def body_code
|
38
|
-
body
|
39
|
-
stmt(compiler.returns(body))
|
38
|
+
stmt(compiler.returns(body || s(:nil)))
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
data/lib/opal/nodes/def.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'opal/nodes/
|
1
|
+
require 'opal/nodes/scope'
|
2
2
|
|
3
3
|
module Opal
|
4
4
|
module Nodes
|
5
5
|
# FIXME: needs rewrite
|
6
|
-
class DefNode <
|
6
|
+
class DefNode < ScopeNode
|
7
7
|
handle :def
|
8
8
|
|
9
9
|
children :recvr, :mid, :args, :stmts
|
@@ -40,7 +40,7 @@ module Opal
|
|
40
40
|
arity_code = arity_check(args, opt, uses_splat, block_name, mid)
|
41
41
|
end
|
42
42
|
|
43
|
-
in_scope
|
43
|
+
in_scope do
|
44
44
|
scope.mid = mid
|
45
45
|
scope.defs = true if recvr
|
46
46
|
|
@@ -53,7 +53,7 @@ module Opal
|
|
53
53
|
scope.block_name = yielder
|
54
54
|
|
55
55
|
params = process(args)
|
56
|
-
stmt_code = stmt(stmts)
|
56
|
+
stmt_code = stmt(compiler.returns(stmts))
|
57
57
|
|
58
58
|
add_temp 'self = this'
|
59
59
|
|
@@ -13,20 +13,6 @@ module Opal
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
# :scope nodes are actually inside scopes (e.g. :module, :class).
|
17
|
-
# These are not actually the scopes themselves.
|
18
|
-
class ScopeNode < Base
|
19
|
-
handle :scope
|
20
|
-
|
21
|
-
children :body
|
22
|
-
|
23
|
-
def compile
|
24
|
-
body = self.body || s(:nil)
|
25
|
-
body = compiler.returns(body) unless scope.class_scope?
|
26
|
-
push stmt(body)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
16
|
class UndefNode < Base
|
31
17
|
handle :undef
|
32
18
|
|
data/lib/opal/nodes/iter.rb
CHANGED
@@ -1,25 +1,15 @@
|
|
1
|
-
require 'opal/nodes/
|
1
|
+
require 'opal/nodes/scope'
|
2
2
|
|
3
3
|
module Opal
|
4
4
|
module Nodes
|
5
|
-
|
6
|
-
class IterNode < BaseScopeNode
|
5
|
+
class IterNode < ScopeNode
|
7
6
|
handle :iter
|
8
7
|
|
9
8
|
children :args_sexp, :body_sexp
|
10
9
|
|
11
10
|
def compile
|
12
|
-
|
13
|
-
|
14
|
-
opt_args = args.pop
|
15
|
-
opt_args.shift
|
16
|
-
end
|
17
|
-
|
18
|
-
# does this iter define a block_pass
|
19
|
-
if args.last.is_a?(Sexp) and args.last.type == :block_pass
|
20
|
-
block_arg = args.pop
|
21
|
-
block_arg = block_arg[1][1].to_sym
|
22
|
-
end
|
11
|
+
opt_args = extract_opt_args
|
12
|
+
block_arg = extract_block_arg
|
23
13
|
|
24
14
|
# find any splat args
|
25
15
|
if args.last.is_a?(Sexp) and args.last.type == :splat
|
@@ -31,31 +21,13 @@ module Opal
|
|
31
21
|
params = args_to_params(args[1..-1])
|
32
22
|
params << splat if splat
|
33
23
|
|
34
|
-
to_vars = identity = nil
|
24
|
+
to_vars = identity = body_code = nil
|
35
25
|
|
36
|
-
in_scope
|
26
|
+
in_scope do
|
37
27
|
identity = scope.identify!
|
38
28
|
add_temp "self = #{identity}._s || this"
|
39
29
|
|
40
|
-
|
41
|
-
args[1..-1].each_with_index do |arg, idx|
|
42
|
-
if arg.type == :lasgn
|
43
|
-
arg = variable(arg[1])
|
44
|
-
|
45
|
-
if opt_args and current_opt = opt_args.find { |s| s[1] == arg.to_sym }
|
46
|
-
push "if (#{arg} == null) #{arg} = ", expr(current_opt[2]), ";"
|
47
|
-
else
|
48
|
-
push "if (#{arg} == null) #{arg} = nil;"
|
49
|
-
end
|
50
|
-
elsif arg.type == :array
|
51
|
-
arg[1..-1].each_with_index do |_arg, _idx|
|
52
|
-
_arg = variable(_arg[1])
|
53
|
-
push "#{_arg} = #{params[idx]}[#{_idx}];"
|
54
|
-
end
|
55
|
-
else
|
56
|
-
raise "Bad block arg type"
|
57
|
-
end
|
58
|
-
end
|
30
|
+
compile_args(args[1..-1], opt_args, params)
|
59
31
|
|
60
32
|
if splat
|
61
33
|
scope.add_arg splat
|
@@ -70,13 +42,54 @@ module Opal
|
|
70
42
|
line "#{block_arg} = #{scope_name}._p || nil, #{scope_name}._p = null;"
|
71
43
|
end
|
72
44
|
|
73
|
-
|
45
|
+
body_code = stmt(body)
|
74
46
|
to_vars = scope.to_vars
|
75
47
|
end
|
76
48
|
|
49
|
+
line body_code
|
50
|
+
|
77
51
|
unshift to_vars
|
78
|
-
|
79
|
-
|
52
|
+
|
53
|
+
unshift "(#{identity} = function(#{params.join ', '}){"
|
54
|
+
push "}, #{identity}._s = self, #{identity})"
|
55
|
+
end
|
56
|
+
|
57
|
+
def compile_args(args, opt_args, params)
|
58
|
+
args.each_with_index do |arg, idx|
|
59
|
+
if arg.type == :lasgn
|
60
|
+
arg = variable(arg[1])
|
61
|
+
|
62
|
+
if opt_args and current_opt = opt_args.find { |s| s[1] == arg.to_sym }
|
63
|
+
push "if (#{arg} == null) #{arg} = ", expr(current_opt[2]), ";"
|
64
|
+
else
|
65
|
+
push "if (#{arg} == null) #{arg} = nil;"
|
66
|
+
end
|
67
|
+
elsif arg.type == :array
|
68
|
+
arg[1..-1].each_with_index do |_arg, _idx|
|
69
|
+
_arg = variable(_arg[1])
|
70
|
+
push "#{_arg} = #{params[idx]}[#{_idx}];"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
raise "Bad block arg type"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# opt args are last (if present) and are a s(:block)
|
79
|
+
def extract_opt_args
|
80
|
+
if args.last.is_a?(Sexp) and args.last.type == :block
|
81
|
+
opt_args = args.pop
|
82
|
+
opt_args.shift
|
83
|
+
opt_args
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# does this iter define a block_pass
|
88
|
+
def extract_block_arg
|
89
|
+
if args.last.is_a?(Sexp) and args.last.type == :block_pass
|
90
|
+
block_arg = args.pop
|
91
|
+
block_arg = block_arg[1][1].to_sym
|
92
|
+
end
|
80
93
|
end
|
81
94
|
|
82
95
|
def args
|
data/lib/opal/nodes/literal.rb
CHANGED
@@ -6,51 +6,56 @@ module Opal
|
|
6
6
|
handle :true, :false, :self, :nil
|
7
7
|
|
8
8
|
def compile
|
9
|
-
# :self, :true, :false, :nil
|
10
9
|
push type.to_s
|
11
10
|
end
|
12
11
|
end
|
13
12
|
|
14
|
-
class
|
15
|
-
children :value
|
16
|
-
end
|
17
|
-
|
18
|
-
class NumericNode < LiteralNode
|
13
|
+
class NumericNode < Base
|
19
14
|
handle :int, :float
|
20
15
|
|
16
|
+
children :value
|
17
|
+
|
21
18
|
def compile
|
22
19
|
push value.to_s
|
23
20
|
wrap '(', ')' if recv?
|
24
21
|
end
|
25
22
|
end
|
26
23
|
|
27
|
-
class StringNode <
|
24
|
+
class StringNode < Base
|
28
25
|
handle :str
|
29
26
|
|
27
|
+
children :value
|
28
|
+
|
30
29
|
def compile
|
31
30
|
push value.inspect
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
|
-
class SymbolNode <
|
34
|
+
class SymbolNode < Base
|
36
35
|
handle :sym
|
37
36
|
|
37
|
+
children :value
|
38
|
+
|
38
39
|
def compile
|
39
40
|
push value.to_s.inspect
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
class RegexpNode <
|
44
|
+
class RegexpNode < Base
|
44
45
|
handle :regexp
|
45
46
|
|
47
|
+
children :value
|
48
|
+
|
46
49
|
def compile
|
47
50
|
push((value == // ? /^/ : value).inspect)
|
48
51
|
end
|
49
52
|
end
|
50
53
|
|
51
|
-
class XStringNode <
|
54
|
+
class XStringNode < Base
|
52
55
|
handle :xstr
|
53
56
|
|
57
|
+
children :value
|
58
|
+
|
54
59
|
def needs_semicolon?
|
55
60
|
stmt? and !value.to_s.include?(';')
|
56
61
|
end
|
@@ -158,35 +163,27 @@ module Opal
|
|
158
163
|
end
|
159
164
|
end
|
160
165
|
|
161
|
-
class
|
162
|
-
handle :
|
166
|
+
class InclusiveRangeNode < Base
|
167
|
+
handle :irange
|
163
168
|
|
164
169
|
children :start, :finish
|
165
170
|
|
166
171
|
def compile
|
167
172
|
helper :range
|
168
173
|
|
169
|
-
push
|
170
|
-
push expr(start)
|
171
|
-
push ", "
|
172
|
-
push expr(finish)
|
173
|
-
push ", false)"
|
174
|
+
push '$range(', expr(start), ', ', expr(finish), ', false)'
|
174
175
|
end
|
175
176
|
end
|
176
177
|
|
177
|
-
class
|
178
|
-
handle :
|
178
|
+
class ExclusiveRangeNode < Base
|
179
|
+
handle :erange
|
179
180
|
|
180
181
|
children :start, :finish
|
181
182
|
|
182
183
|
def compile
|
183
184
|
helper :range
|
184
185
|
|
185
|
-
push
|
186
|
-
push expr(start)
|
187
|
-
push ", "
|
188
|
-
push expr(finish)
|
189
|
-
push ", true)"
|
186
|
+
push '$range(', expr(start), ', ', expr(finish), ', true)'
|
190
187
|
end
|
191
188
|
end
|
192
189
|
end
|