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.
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