liquidscript 0.11.0.rc1 → 0.11.0
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/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
|