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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/liquidscript.rb +1 -1
  3. data/lib/liquidscript/cli.rb +1 -1
  4. data/lib/liquidscript/compiler/base.rb +7 -0
  5. data/lib/liquidscript/compiler/base/blank.rb +7 -0
  6. data/lib/liquidscript/compiler/icr/classes.rb +46 -30
  7. data/lib/liquidscript/compiler/icr/directives.rb +42 -4
  8. data/lib/liquidscript/compiler/icr/expressions.rb +40 -6
  9. data/lib/liquidscript/compiler/icr/functions.rb +7 -11
  10. data/lib/liquidscript/compiler/icr/groups.rb +2 -1
  11. data/lib/liquidscript/compiler/icr/helpers.rb +8 -0
  12. data/lib/liquidscript/compiler/icr/literals.rb +9 -4
  13. data/lib/liquidscript/errors.rb +9 -8
  14. data/lib/liquidscript/generator/javascript/exceptions.rb +5 -1
  15. data/lib/liquidscript/generator/javascript/literals.rb +33 -1
  16. data/lib/liquidscript/generator/javascript/metas.rb +14 -1
  17. data/lib/liquidscript/generator/javascript/objects.rb +3 -0
  18. data/lib/liquidscript/icr/code.rb +43 -2
  19. data/lib/liquidscript/icr/context.rb +163 -85
  20. data/lib/liquidscript/icr/representable.rb +1 -1
  21. data/lib/liquidscript/icr/set.rb +43 -81
  22. data/lib/liquidscript/icr/variable.rb +17 -0
  23. data/lib/liquidscript/scanner/base.rb +11 -1
  24. data/lib/liquidscript/scanner/base/lexer.rb +3 -2
  25. data/lib/liquidscript/scanner/liquidscript/main.rb +36 -34
  26. data/lib/liquidscript/version.rb +1 -1
  27. data/spec/fixtures/class.compile.yml +0 -2
  28. data/spec/fixtures/function.generate.yml +14 -1
  29. data/spec/fixtures/literals.generate.yml +3 -1
  30. data/spec/liquidscript/compiler/icr_spec.rb +1 -1
  31. data/spec/liquidscript/icr/context_spec.rb +2 -2
  32. data/spec/liquidscript/icr/set_spec.rb +4 -4
  33. data/vendor/assets/javascripts/liquidscript.js +103 -0
  34. data/vendor/assets/javascripts/liquidscript.liq +29 -0
  35. data/vendor/assets/javascripts/promise.liq +67 -0
  36. 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].value}) {\n"
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 << "#{indent_level}var #{code.locals.join(', ')};\n"
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, *@arguments]
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
- # The variables that are a part of this context.
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
- @allowed_variables = [DEFAULT_ALLOWED_VARIABLES].flatten
53
- @parents = []
34
+ @undefined = []
35
+ end
36
+
37
+ def allowed_variables
38
+ DEFAULT_ALLOWED_VARIABLES.dup
54
39
  end
55
40
 
56
- # Returns a variable reference. If checks the local variables
57
- # first; if it doesn't exist there, then if the type is `:get`,
58
- # checks the parent; otherwise, sets the value of the variable.
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
- # @param name [Symbol] the name of the variable.
63
- # @param type [Symbol] the type of use. Should be `:get` or
64
- # `:set`.
65
- # @return [Variable]
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
- # Allows a specific variable to be used - but doesn't define it
73
- # in the current context.
51
+ # Sets whether or not the context will delegate setting
52
+ # variables to its parent.
74
53
  #
75
- # @param name [Symbol]
76
- # @return [void]
77
- def allow(name)
78
- allowed_variables << name
54
+ # @see {#delegate?}
55
+ # @return [Boolean]
56
+ def delegate!
57
+ @delegate = !delegate?
79
58
  end
80
59
 
81
- # All of the parameter variables.
60
+ # Delegates a block, such that the contents don't affect
61
+ # the current context.
82
62
  #
83
- # @return [Array<Variable>]
84
- def parameters
85
- @variables.values.select(&:parameter?)
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
- # (see #variable).
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
- # Passes `:get` as type.
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
- @variables.fetch(name) do
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
- when parents.any?
97
- get_parent(name)
98
- when @class
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
- # (see #variable).
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
- # Passes `:set` as type.
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
- @variables.fetch(name) do
151
+ return parent.set(name, options) if delegate?
152
+
153
+ variables.fetch(name) do
111
154
  @undefined.reject! { |(n, _)| n == name }
112
- @variables[name] =
113
- Variable.new(self, name,
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
- def class!
119
- @class = true
120
- self
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
- @variables.keys
182
+ variables.keys
125
183
  end
126
184
 
127
- private
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
- def get_parent(name)
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
- where = results.detect { |i|
139
- not i.is_a?(InvalidReferenceError) }
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
- if where
142
- where
211
+ rescue InvalidReferenceError => e
212
+ if class? && !options[:dry_run]
213
+ add_undefined(name, e)
143
214
  else
144
- error = results.first
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
- def add_undefined(name, e = InvalidReferenceError.new(name))
154
- @undefined << [name, e]
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