liquidscript 0.11.0.rc1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/liquidscript.rb +1 -1
- data/lib/liquidscript/cli.rb +1 -1
- data/lib/liquidscript/compiler/base.rb +7 -0
- data/lib/liquidscript/compiler/base/blank.rb +7 -0
- data/lib/liquidscript/compiler/icr/classes.rb +46 -30
- data/lib/liquidscript/compiler/icr/directives.rb +42 -4
- data/lib/liquidscript/compiler/icr/expressions.rb +40 -6
- data/lib/liquidscript/compiler/icr/functions.rb +7 -11
- data/lib/liquidscript/compiler/icr/groups.rb +2 -1
- data/lib/liquidscript/compiler/icr/helpers.rb +8 -0
- data/lib/liquidscript/compiler/icr/literals.rb +9 -4
- data/lib/liquidscript/errors.rb +9 -8
- data/lib/liquidscript/generator/javascript/exceptions.rb +5 -1
- data/lib/liquidscript/generator/javascript/literals.rb +33 -1
- data/lib/liquidscript/generator/javascript/metas.rb +14 -1
- data/lib/liquidscript/generator/javascript/objects.rb +3 -0
- data/lib/liquidscript/icr/code.rb +43 -2
- data/lib/liquidscript/icr/context.rb +163 -85
- data/lib/liquidscript/icr/representable.rb +1 -1
- data/lib/liquidscript/icr/set.rb +43 -81
- data/lib/liquidscript/icr/variable.rb +17 -0
- data/lib/liquidscript/scanner/base.rb +11 -1
- data/lib/liquidscript/scanner/base/lexer.rb +3 -2
- data/lib/liquidscript/scanner/liquidscript/main.rb +36 -34
- data/lib/liquidscript/version.rb +1 -1
- data/spec/fixtures/class.compile.yml +0 -2
- data/spec/fixtures/function.generate.yml +14 -1
- data/spec/fixtures/literals.generate.yml +3 -1
- data/spec/liquidscript/compiler/icr_spec.rb +1 -1
- data/spec/liquidscript/icr/context_spec.rb +2 -2
- data/spec/liquidscript/icr/set_spec.rb +4 -4
- data/vendor/assets/javascripts/liquidscript.js +103 -0
- data/vendor/assets/javascripts/liquidscript.liq +29 -0
- data/vendor/assets/javascripts/promise.liq +67 -0
- metadata +7 -4
@@ -6,7 +6,9 @@ module Liquidscript
|
|
6
6
|
def generate_try(code)
|
7
7
|
out = buffer
|
8
8
|
out << "try {\n"
|
9
|
+
indent!
|
9
10
|
insert_into(code[1], out)
|
11
|
+
unindent!
|
10
12
|
out << indent_level << "}"
|
11
13
|
|
12
14
|
if code[2]
|
@@ -18,8 +20,10 @@ module Liquidscript
|
|
18
20
|
|
19
21
|
def generate_catch(code)
|
20
22
|
out = buffer
|
21
|
-
out << "catch(#{code[1]
|
23
|
+
out << "catch(#{replace(code[1])}) {\n"
|
24
|
+
indent!
|
22
25
|
insert_into(code[2], out)
|
26
|
+
unindent!
|
23
27
|
out << indent_level << "}"
|
24
28
|
|
25
29
|
if code[3]
|
@@ -31,6 +31,19 @@ module Liquidscript
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
def generate_nerange(code)
|
35
|
+
start = code[1]
|
36
|
+
ending = code[2]
|
37
|
+
|
38
|
+
if (start.to_i - ending.to_i).abs > 50
|
39
|
+
generate_erange(code, {}, true)
|
40
|
+
elsif ending > start
|
41
|
+
buffer << "[" << (start...ending).to_a.join(', ') << "]"
|
42
|
+
else
|
43
|
+
buffer << "[" << (ending...start).to_a.reverse.join(', ') << "]"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
34
47
|
def generate_range(code, options = {}, norep = false)
|
35
48
|
out = buffer
|
36
49
|
a = norep ? code[1] : replace(code[1])
|
@@ -44,7 +57,26 @@ module Liquidscript
|
|
44
57
|
indent << "for(i = a; i <= b; i++) {\n" <<
|
45
58
|
indent! << "out.push(i);\n" <<
|
46
59
|
unindent! << "};\n" <<
|
47
|
-
indent << "return t === undefined ?"
|
60
|
+
indent << "return t === undefined ? " <<
|
61
|
+
"out : out.reverse();\n" <<
|
62
|
+
unindent! << "})(" << a << ", " << b << ")"
|
63
|
+
out
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate_erange(code, options = {}, norep = false)
|
67
|
+
out = buffer
|
68
|
+
a = norep ? code[1] : replace(code[1])
|
69
|
+
b = norep ? code[2] : replace(code[2])
|
70
|
+
out << "(function(a, b) {\n" <<
|
71
|
+
indent! << "var out, i, t;\n" <<
|
72
|
+
indent << "out = [];\n" <<
|
73
|
+
indent << "if(a > b) {\n" <<
|
74
|
+
indent! << "t = a; a = b; b = t;\n" <<
|
75
|
+
unindent! << "}\n" <<
|
76
|
+
indent << "for(i = a; i < b; i++) {\n" <<
|
77
|
+
indent! << "out.push(i);\n" <<
|
78
|
+
unindent! << "};\n" <<
|
79
|
+
indent << "return t === undefined ? " <<
|
48
80
|
"out : out.reverse();\n" <<
|
49
81
|
unindent! << "})(" << a << ", " << b << ")"
|
50
82
|
out
|
@@ -71,8 +71,21 @@ module Liquidscript
|
|
71
71
|
|
72
72
|
out << "#{indent}\"use strict\";\n" if code[:strict]
|
73
73
|
unless code.locals.empty?
|
74
|
-
out << "#{
|
74
|
+
out << "#{indent}var #{code.locals.join(', ')};\n"
|
75
75
|
end
|
76
|
+
|
77
|
+
if code[:arguments] and code[:arguments].any?
|
78
|
+
code[:arguments].each do |(k, v)|
|
79
|
+
if v == :etc
|
80
|
+
index = code[:arguments].map(&:first).index(k)
|
81
|
+
out << "#{indent}#{k[1]} = [].slice.call(arguments, #{index});"
|
82
|
+
elsif v
|
83
|
+
out << "#{indent}if(#{k[1]} == null) {\n#{indent!}#{k[1]} = #{replace(v)};\n#{unindent!}}\n"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
out
|
76
89
|
end
|
77
90
|
end
|
78
91
|
end
|
@@ -40,6 +40,7 @@ module Liquidscript
|
|
40
40
|
_context :name => code[1].value,
|
41
41
|
:inherit => code[2],
|
42
42
|
:parts => code[3],
|
43
|
+
:existed => code[:existed],
|
43
44
|
:inheritance => "%{name}.prototype.__proto__ = %{inherit}.prototype;\n",
|
44
45
|
:identifier => "%{name}.prototype.%{value} = %{replace};\n",
|
45
46
|
:istring => "%{name}.prototype[\"%{value}\"] = %{replace};\n",
|
@@ -52,6 +53,7 @@ module Liquidscript
|
|
52
53
|
def generate_module(code)
|
53
54
|
_context :name => code[1].value,
|
54
55
|
:parts => code[2],
|
56
|
+
:existed => code[:existed],
|
55
57
|
:head => "%{name} = %{name} || {};\n",
|
56
58
|
:identifier => "%{name}.%{value} = %{replace};\n",
|
57
59
|
:istring => "%{name}[\"%{value}\"] = %{replace};\n",
|
@@ -120,6 +122,7 @@ module Liquidscript
|
|
120
122
|
end
|
121
123
|
|
122
124
|
def _build_header(body, options, opts)
|
125
|
+
return body if options[:existed]
|
123
126
|
body << indent << sprintf(options[:head], opts)
|
124
127
|
|
125
128
|
if options[:inherit]
|
@@ -19,6 +19,8 @@ module Liquidscript
|
|
19
19
|
# @return [Array]
|
20
20
|
attr_reader :arguments
|
21
21
|
|
22
|
+
attr_reader :metadata
|
23
|
+
|
22
24
|
alias_method :type, :action
|
23
25
|
|
24
26
|
include Representable
|
@@ -32,6 +34,7 @@ module Liquidscript
|
|
32
34
|
def initialize(action, *arguments)
|
33
35
|
@action = action
|
34
36
|
@arguments = arguments
|
37
|
+
@metadata = {}
|
35
38
|
end
|
36
39
|
|
37
40
|
# Turns the code into an array, containing the
|
@@ -40,7 +43,9 @@ module Liquidscript
|
|
40
43
|
#
|
41
44
|
# @return [Array]
|
42
45
|
def to_a
|
43
|
-
[@action
|
46
|
+
part = [@action]
|
47
|
+
part.concat(@arguments)
|
48
|
+
part
|
44
49
|
end
|
45
50
|
|
46
51
|
# If this code respresents something with a definite
|
@@ -51,10 +56,40 @@ module Liquidscript
|
|
51
56
|
@_value ||= ![
|
52
57
|
:class, :module, :if, :elseif, :unless,
|
53
58
|
:else, :try, :catch, :finally, :while, :for_in,
|
54
|
-
:for_seg, :return
|
59
|
+
:for_seg, :return, :exec
|
55
60
|
].include?(@action)
|
56
61
|
end
|
57
62
|
|
63
|
+
# Access either the metadata or the codes. If
|
64
|
+
# the accessor is a Symbol, it access the metadata;
|
65
|
+
# if it the accessor is a Numeric, it access the
|
66
|
+
# codes.
|
67
|
+
#
|
68
|
+
# @param key [Symbol, Numeric] the key.
|
69
|
+
# @return [Object]
|
70
|
+
def [](key)
|
71
|
+
if argument_key?(key)
|
72
|
+
super
|
73
|
+
else
|
74
|
+
@metadata[key]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sets something from the metadata. Unlike the
|
79
|
+
# accessor, it does not distinguish between
|
80
|
+
# Numeric and Symbol keys.
|
81
|
+
#
|
82
|
+
# @param key [Object] the key.
|
83
|
+
# @param value [Object] the value.
|
84
|
+
# @return [Object]
|
85
|
+
def []=(key, value)
|
86
|
+
if argument_key?(key)
|
87
|
+
super
|
88
|
+
else
|
89
|
+
@metadata[key] = value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
58
93
|
# If we don't respond to it, the @arguments array
|
59
94
|
# might. Ask them if they do, and if they don't,
|
60
95
|
# respond accordingly.
|
@@ -75,6 +110,12 @@ module Liquidscript
|
|
75
110
|
@arguments.public_send(method, *args, &block)
|
76
111
|
end
|
77
112
|
|
113
|
+
private
|
114
|
+
|
115
|
+
def argument_key?(key)
|
116
|
+
key.is_a?(Numeric) or key.is_a?(Range)
|
117
|
+
end
|
118
|
+
|
78
119
|
end
|
79
120
|
end
|
80
121
|
end
|
@@ -11,6 +11,8 @@ module Liquidscript
|
|
11
11
|
# forcibly created.
|
12
12
|
class Context
|
13
13
|
|
14
|
+
include Representable
|
15
|
+
|
14
16
|
# The variables that are allowed to be used as a global scope,
|
15
17
|
# i.e. used in a `get` context without a previous `set`.
|
16
18
|
DEFAULT_ALLOWED_VARIABLES = %w(
|
@@ -22,138 +24,214 @@ module Liquidscript
|
|
22
24
|
Date String RegExp Array Float32Array Float64Array Int16Array
|
23
25
|
Int32Array Int8Array Uint16Array Uint32Array Uint8Array
|
24
26
|
Uint8ClampedArray ArrayBuffer DataView JSON Intl
|
25
|
-
).map(&:intern)
|
26
|
-
|
27
|
-
# The parent of the current context.
|
28
|
-
#
|
29
|
-
# @return [Parent]
|
30
|
-
attr_accessor :parents
|
27
|
+
).map(&:intern).freeze
|
31
28
|
|
32
|
-
|
33
|
-
#
|
34
|
-
# @return [Hash<Symbol, Variable>]
|
29
|
+
attr_accessor :parent
|
35
30
|
attr_reader :variables
|
36
31
|
|
37
|
-
# The variables that are allowed to be used as a global scope,
|
38
|
-
# i.e. used in a `get` context without a previous set.
|
39
|
-
#
|
40
|
-
# @see [DEFAULT_ALLOWED_VARIABLES]
|
41
|
-
# @return [Array<Symbol>]
|
42
|
-
attr_reader :allowed_variables
|
43
|
-
|
44
|
-
attr_reader :undefined
|
45
|
-
|
46
|
-
include Representable
|
47
|
-
|
48
|
-
# Initializes the context.
|
49
32
|
def initialize
|
50
|
-
@undefined = []
|
51
33
|
@variables = {}
|
52
|
-
@
|
53
|
-
|
34
|
+
@undefined = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def allowed_variables
|
38
|
+
DEFAULT_ALLOWED_VARIABLES.dup
|
54
39
|
end
|
55
40
|
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# If there is no parent and the type is `:get`, it raises an
|
60
|
-
# {InvalidReferenceError}.
|
41
|
+
# If the context delegates setting variables to its parents.
|
42
|
+
# This keeps this context from getting any variables set on
|
43
|
+
# it, and instead sets variables on the parent.
|
61
44
|
#
|
62
|
-
# @
|
63
|
-
# @
|
64
|
-
|
65
|
-
|
66
|
-
def variable(name, type, options = {})
|
67
|
-
if [:get, :set].include?(type)
|
68
|
-
send(type, name, options = {})
|
69
|
-
end
|
45
|
+
# @see {#delegate!}
|
46
|
+
# @return [Boolean]
|
47
|
+
def delegate?
|
48
|
+
@delegate
|
70
49
|
end
|
71
50
|
|
72
|
-
#
|
73
|
-
#
|
51
|
+
# Sets whether or not the context will delegate setting
|
52
|
+
# variables to its parent.
|
74
53
|
#
|
75
|
-
# @
|
76
|
-
# @return [
|
77
|
-
def
|
78
|
-
|
54
|
+
# @see {#delegate?}
|
55
|
+
# @return [Boolean]
|
56
|
+
def delegate!
|
57
|
+
@delegate = !delegate?
|
79
58
|
end
|
80
59
|
|
81
|
-
#
|
60
|
+
# Delegates a block, such that the contents don't affect
|
61
|
+
# the current context.
|
82
62
|
#
|
83
|
-
# @return [
|
84
|
-
def
|
85
|
-
@
|
63
|
+
# @return [Object] the value of the block
|
64
|
+
def delegate
|
65
|
+
old, @delegate = @delegate, true
|
66
|
+
out = yield
|
67
|
+
@delegate = old
|
68
|
+
out
|
86
69
|
end
|
87
70
|
|
88
|
-
#
|
71
|
+
# If this context is associated with a class. The context
|
72
|
+
# will forward any errors until after the context is completely
|
73
|
+
# finalized.
|
89
74
|
#
|
90
|
-
#
|
75
|
+
# @see {#class!}
|
76
|
+
# @return [Boolean]
|
77
|
+
def class?
|
78
|
+
@class
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sets this context to be associated with a class.
|
82
|
+
# @see {#class?}
|
83
|
+
# @return [Boolean]
|
84
|
+
def class!
|
85
|
+
@class = true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Retrieves a reference to a variable. If the local
|
89
|
+
# context doesn't have a reference, then it will try
|
90
|
+
# a few things; first, it will check to see if that
|
91
|
+
# variable is one of our allowed variables; second,
|
92
|
+
# it will check if the parent has a reference; otherwise,
|
93
|
+
# it will add an undefined reference if this context
|
94
|
+
# is associated with a class.
|
95
|
+
#
|
96
|
+
# @see {#parent}
|
97
|
+
# @see {#allowed_variables}
|
98
|
+
# @see {#add_undefined}
|
99
|
+
# @see {#variables}
|
100
|
+
# @param name [Symbol] the variable to reference.
|
101
|
+
# @param options [Hash] Extra options.
|
102
|
+
# @option options [Boolean] :dry_run if this is a dry
|
103
|
+
# run. In that case, it won't add an undefined
|
104
|
+
# reference.
|
105
|
+
# @raise [InvalidReferenceError] if the variable could
|
106
|
+
# not be handled correctly.
|
107
|
+
# @return [Variable, Boolean]
|
91
108
|
def get(name, options = {})
|
92
|
-
|
109
|
+
variables.fetch(name) do
|
93
110
|
case true
|
111
|
+
# If the asking variable is an allowed variable, we'll
|
112
|
+
# allow it, and just return a variable instance.
|
94
113
|
when allowed_variables.include?(name)
|
95
|
-
Variable.new(self, name)
|
96
|
-
|
97
|
-
|
98
|
-
|
114
|
+
Variable.new(self, name, :allowed => true)
|
115
|
+
# If we have a parent, we can ask the parent for the
|
116
|
+
# variable. This takes precedence over the class
|
117
|
+
# so we can get a proper reference to the correct
|
118
|
+
# variable.
|
119
|
+
when !!parent
|
120
|
+
parent_get(name, options)
|
121
|
+
# If this context is associated with a class, and
|
122
|
+
# we're not doing a dry run, then we'll add an
|
123
|
+
# undefined.
|
124
|
+
when @class && !options[:dry_run]
|
99
125
|
add_undefined(name)
|
126
|
+
# If we are doing a dry run, however, then just let
|
127
|
+
# the caller know that it would have been successful.
|
128
|
+
when @class && options[:dry_run]
|
129
|
+
true
|
130
|
+
# If none of those options fit, raise an error.
|
100
131
|
else
|
101
132
|
raise InvalidReferenceError.new(name)
|
102
133
|
end
|
103
134
|
end
|
104
135
|
end
|
105
136
|
|
106
|
-
#
|
137
|
+
# If this context delegates, it delegates to the parent;
|
138
|
+
# otherwise, it will check if we have set this variable
|
139
|
+
# before. Otherwise, it will first reject any undefined
|
140
|
+
# variables that existed with the specific name, and then
|
141
|
+
# create a new variable with the given options.
|
107
142
|
#
|
108
|
-
#
|
143
|
+
# @see {#variables}
|
144
|
+
# @see {#parent}
|
145
|
+
# @see {#delegate?}
|
146
|
+
# @param name [Symbol] the name of the variable.
|
147
|
+
# @param options [Hash] the options to pass to the new
|
148
|
+
# variable instance.
|
149
|
+
# @return [Variable]
|
109
150
|
def set(name, options = {})
|
110
|
-
|
151
|
+
return parent.set(name, options) if delegate?
|
152
|
+
|
153
|
+
variables.fetch(name) do
|
111
154
|
@undefined.reject! { |(n, _)| n == name }
|
112
|
-
|
113
|
-
|
114
|
-
{:class => @class}.merge(options))
|
155
|
+
variables[name] = Variable.new(self, name,
|
156
|
+
{ :class => class? }.merge(options))
|
115
157
|
end
|
116
158
|
end
|
117
159
|
|
118
|
-
|
119
|
-
|
120
|
-
|
160
|
+
# Retrieves all of the variables that are parameters
|
161
|
+
# in the current context.
|
162
|
+
#
|
163
|
+
# @see {Variable#parameter?}
|
164
|
+
# @return [Array<Variable>]
|
165
|
+
def parameters
|
166
|
+
variables.values.select(&:parameter?)
|
121
167
|
end
|
122
168
|
|
169
|
+
# Retrieves all of the variables that are hidden in
|
170
|
+
# the current context.
|
171
|
+
#
|
172
|
+
# @see {Variable#hidden?}
|
173
|
+
# @return [Array<Variable>]
|
174
|
+
def hidden
|
175
|
+
variables.values.select(&:hidden?)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the name of the variables in this context.
|
179
|
+
#
|
180
|
+
# @return [Array<Symbol>]
|
123
181
|
def to_a
|
124
|
-
|
182
|
+
variables.keys
|
125
183
|
end
|
126
184
|
|
127
|
-
|
185
|
+
# Check if there are any undefined variables, and if
|
186
|
+
# there are, raise the first one it sees.
|
187
|
+
#
|
188
|
+
#
|
189
|
+
def force_defined!
|
190
|
+
@undefined.each { |f| raise f[1] }
|
191
|
+
end
|
128
192
|
|
129
|
-
|
130
|
-
results = parents.map do |parent|
|
131
|
-
begin
|
132
|
-
parent.get(name)
|
133
|
-
rescue InvalidReferenceError => e
|
134
|
-
e
|
135
|
-
end
|
136
|
-
end.compact
|
193
|
+
private
|
137
194
|
|
138
|
-
|
139
|
-
|
195
|
+
# Retrieves a variable from a parent. If this context
|
196
|
+
# is associated with a class, it will add an undefined
|
197
|
+
# variable if the parent raises an
|
198
|
+
# InvalidReferenceError. Otherwise, it will just allow
|
199
|
+
# the error to be raised.
|
200
|
+
#
|
201
|
+
# @see {#parent}
|
202
|
+
# @see {#add_undefined}
|
203
|
+
# @raise [InvalidReferenceError]
|
204
|
+
# @param name [Symbol] the name of the variable.
|
205
|
+
# @param options [Hash] the options to be passed to the
|
206
|
+
# parent.
|
207
|
+
# @return [Variable]
|
208
|
+
def parent_get(name, options)
|
209
|
+
parent.get(name, options)
|
140
210
|
|
141
|
-
|
142
|
-
|
211
|
+
rescue InvalidReferenceError => e
|
212
|
+
if class? && !options[:dry_run]
|
213
|
+
add_undefined(name, e)
|
143
214
|
else
|
144
|
-
|
145
|
-
if @class
|
146
|
-
add_undefined(name, error)
|
147
|
-
else
|
148
|
-
raise error
|
149
|
-
end
|
215
|
+
raise
|
150
216
|
end
|
151
217
|
end
|
152
218
|
|
153
|
-
|
154
|
-
|
219
|
+
# Adds an undefined variable to the list. If there are
|
220
|
+
# any at the end of compiling, then the corresponding
|
221
|
+
# errors will be raised. This only applies if the context
|
222
|
+
# is associated with a class.
|
223
|
+
#
|
224
|
+
# @see {#class?}
|
225
|
+
# @param name [Symbol] the name of the variable.
|
226
|
+
# @param error [InvalidReferenceError] the error to be
|
227
|
+
# raised if the undefined variable is not defined
|
228
|
+
# by the end of compilation.
|
229
|
+
# @return [Variable]
|
230
|
+
def add_undefined(name, error = InvalidReferenceError.new(name))
|
231
|
+
@undefined << [name, error]
|
155
232
|
Variable.new(self, name, :class => true)
|
156
233
|
end
|
234
|
+
|
157
235
|
end
|
158
236
|
|
159
237
|
end
|