liquidscript 0.4.1 → 0.5.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/liquidscript/buffer.rb +2 -2
  3. data/lib/liquidscript/compiler/icr.rb +1 -0
  4. data/lib/liquidscript/compiler/icr/classes.rb +11 -3
  5. data/lib/liquidscript/compiler/icr/expressions.rb +56 -10
  6. data/lib/liquidscript/compiler/icr/functions.rb +7 -6
  7. data/lib/liquidscript/compiler/icr/heredoc.rb +28 -0
  8. data/lib/liquidscript/compiler/icr/literals.rb +102 -9
  9. data/lib/liquidscript/errors.rb +6 -0
  10. data/lib/liquidscript/generator/base.rb +1 -1
  11. data/lib/liquidscript/generator/base/replacements.rb +2 -2
  12. data/lib/liquidscript/generator/javascript.rb +21 -0
  13. data/lib/liquidscript/generator/javascript/exceptions.rb +42 -0
  14. data/lib/liquidscript/generator/javascript/literals.rb +71 -9
  15. data/lib/liquidscript/generator/javascript/metas.rb +14 -13
  16. data/lib/liquidscript/generator/javascript/objects.rb +21 -16
  17. data/lib/liquidscript/icr/set.rb +8 -0
  18. data/lib/liquidscript/scanner/base/lexer.rb +13 -10
  19. data/lib/liquidscript/scanner/liquidscript.rb +95 -17
  20. data/lib/liquidscript/template.rb +1 -2
  21. data/lib/liquidscript/version.rb +1 -1
  22. data/liquidscript.gemspec +1 -0
  23. data/spec/fixtures/class.compile.yml +12 -1
  24. data/spec/fixtures/class.generate.yml +4 -4
  25. data/spec/fixtures/combination.generate.yml +4 -4
  26. data/spec/fixtures/complex.generate.yml +5 -5
  27. data/spec/fixtures/expression.generate.yml +1 -1
  28. data/spec/fixtures/heredoc.generate.yml +9 -0
  29. data/spec/fixtures/literals.generate.yml +9 -1
  30. data/spec/fixtures/loop.generate.yml +16 -0
  31. data/spec/fixtures/main.compile.yml +19 -15
  32. data/spec/fixtures/operator.generate.yml +10 -2
  33. data/spec/fixtures/string.compile.yml +20 -1
  34. data/spec/fixtures/string.generate.yml +1 -1
  35. data/spec/fixtures/underscore.js +28 -0
  36. data/spec/fixtures/underscore.ls +2 -1
  37. data/spec/liquidscript/compiler/icr_spec.rb +2 -0
  38. data/spec/liquidscript/node_spec.rb +21 -0
  39. data/spec/liquidscript/scanner/lexer_spec.rb +23 -2
  40. data/spec/support/matchers/compile.rb +1 -1
  41. data/spec/support/matchers/generate.rb +6 -3
  42. data/spec/support/matchers/run.rb +12 -0
  43. metadata +28 -2
@@ -0,0 +1,42 @@
1
+ module Liquidscript
2
+ module Generator
3
+ class Javascript < Base
4
+ module Exceptions
5
+
6
+ def generate_try(code)
7
+ out = buffer
8
+ out << "try {\n"
9
+ insert_into(code[1], out)
10
+ out << indent_level << "}"
11
+
12
+ if code[2]
13
+ out << replace(code[2])
14
+ end
15
+
16
+ out
17
+ end
18
+
19
+ def generate_catch(code)
20
+ out = buffer
21
+ out << "catch(#{code[1].value}) {\n"
22
+ insert_into(code[2], out)
23
+ out << indent_level << "}"
24
+
25
+ if code[3]
26
+ out << replace(code[3])
27
+ end
28
+
29
+ out
30
+ end
31
+
32
+ def generate_finally(code)
33
+ out = buffer
34
+ out << "finally {\n"
35
+ insert_into(code[1], out)
36
+ out << indent_level << "}"
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,19 +4,60 @@ module Liquidscript
4
4
  module Literals
5
5
 
6
6
  BINOP_SWITCH = {
7
- "==" => "===",
7
+ "==" => "===",
8
8
  "===" => "==",
9
- "!=" => "!==",
9
+ "!=" => "!==",
10
10
  "!==" => "!=",
11
- "or" => "||",
11
+ "or" => "||",
12
12
  "and" => "&&"
13
13
  }.freeze
14
14
 
15
15
  def generate_number(code)
16
-
17
16
  "#{code.first}"
18
17
  end
19
18
 
19
+ def generate_action(code)
20
+ code[1].value
21
+ end
22
+
23
+ def generate_op(code)
24
+ if code[1].type == :variable
25
+ "#{code[1].name}#{code[2].value}"
26
+ else
27
+ "#{replace(code[1])}#{code[2].value}"
28
+ end
29
+ end
30
+
31
+ def generate_while(code)
32
+ loop_body = buffer
33
+ loop_body << "while(#{replace(code[1])}) {\n"
34
+ insert_into(code[2], loop_body)
35
+ loop_body << indent_level << "}"
36
+ end
37
+
38
+ def generate_for_in(code)
39
+ loop_body = buffer
40
+ loop_body << "for(#{code[1].value} in #{code[2].name}) {\n"
41
+ insert_into(code[3], loop_body)
42
+ loop_body << indent_level << "}"
43
+ end
44
+
45
+ def generate_for_seg(code)
46
+ loop_body = buffer
47
+ loop_body << "for(" <<
48
+ replace(code[1]) << ";" <<
49
+ replace(code[2]) << ";" <<
50
+ replace(code[3]) << ") {\n"
51
+
52
+ insert_into(code[4], loop_body)
53
+
54
+ loop_body << indent_level << "}"
55
+ end
56
+
57
+ def generate_regex(code)
58
+ "/#{code[1].value[0]}/#{code[1].value[1]}"
59
+ end
60
+
20
61
  def generate_istring(code)
21
62
  "\"#{code.first.gsub("\n", "\\n")}\""
22
63
  end
@@ -24,18 +65,17 @@ module Liquidscript
24
65
  def generate_interop(code)
25
66
  content = code[1..-1]
26
67
  buf = buffer
27
- buf.set_join! ','
28
68
 
29
69
  content.each do |part|
30
70
  case part.type
31
71
  when :istring_begin, :istring
32
72
  buf << "\"#{part.value}\""
33
73
  else
34
- buf << "#{replace(part)}"
74
+ buf << " + (#{replace(part)}) + "
35
75
  end
36
76
  end
37
77
 
38
- "[#{buf}].join('')"
78
+ buf
39
79
  end
40
80
 
41
81
  def generate_sstring(code)
@@ -47,6 +87,22 @@ module Liquidscript
47
87
  " #{code[1].value} #{replace(code[2])}"
48
88
  end
49
89
 
90
+ def generate_href(code)
91
+ heredoc = code[1]
92
+ hbuf = buffer
93
+
94
+ heredoc.body.each do |part|
95
+ case part.type
96
+ when :heredoc, :iheredoc, :iheredoc_begin
97
+ hbuf << '"' << part.value.gsub('"', '\\"') << '"'
98
+ else
99
+ hbuf << ' + ' << replace(part) << ' + '
100
+ end
101
+ end
102
+
103
+ hbuf
104
+ end
105
+
50
106
  def generate_binop(code)
51
107
  op = BINOP_SWITCH.fetch(code[1].value) do
52
108
  code[1].value
@@ -63,11 +119,13 @@ module Liquidscript
63
119
 
64
120
  object = buffer
65
121
  object.set_join! ', '
122
+ indent!
66
123
 
67
124
  code[1].inject(object) do |buf, part|
68
- buf << "\"#{part[0].value}\": #{replace(part[1])}"
125
+ buf << "#{indent_level}\"#{part[0].value}\": #{replace(part[1])}"
69
126
  end
70
127
 
128
+ unindent!
71
129
  "{#{object}}"
72
130
  end
73
131
 
@@ -84,12 +142,13 @@ module Liquidscript
84
142
  end
85
143
 
86
144
  def generate_newline(_)
87
- "\n"
145
+ ""
88
146
  end
89
147
 
90
148
  def generate_function(code)
91
149
 
92
150
  function = buffer
151
+ indent!
93
152
 
94
153
  function <<
95
154
  "function(" <<
@@ -97,6 +156,9 @@ module Liquidscript
97
156
  ') {' <<
98
157
  replace(code[1]) <<
99
158
  '}'
159
+
160
+ unindent!
161
+ function
100
162
  end
101
163
  end
102
164
  end
@@ -6,18 +6,15 @@ module Liquidscript
6
6
  def generate_exec(code)
7
7
  exec = buffer
8
8
  exec << _exec_context(code)
9
- code.codes.inject(exec) do |m, c|
10
- m << replace(c)
11
- m
12
- end
9
+ insert_into(code.codes, exec)
13
10
  end
14
11
 
15
12
  def generate_set(code)
16
13
  case code[1].type
17
14
  when :variable
18
- "#{code[1].name} = #{replace code[2]};"
15
+ "#{code[1].name} = #{replace code[2]}"
19
16
  when :property
20
- "#{replace code[1]} = #{replace code[2]};"
17
+ "#{replace code[1]} = #{replace code[2]}"
21
18
  end
22
19
  end
23
20
 
@@ -31,9 +28,11 @@ module Liquidscript
31
28
 
32
29
  define_method(:"generate_#{k}") do |code|
33
30
  part = buffer
34
- part << "\n#{v % replace(code[1])} {\n"
35
- code[2].inject(part) { |m, p| m << replace(p) }
36
- part << "\n}\n"
31
+ part << "#{v % replace(code[1])} {\n"
32
+ indent!
33
+ insert_into(code[2], part)
34
+ unindent!
35
+ part << indent_level << "}"
37
36
 
38
37
  if code[3]
39
38
  part << replace(code[3])
@@ -45,9 +44,11 @@ module Liquidscript
45
44
 
46
45
  def generate_else(code)
47
46
  part = buffer
48
- part << "\nelse {\n"
49
- code[1].inject(part) { |m, p| m << replace(p) }
50
- part << "\n}\n"
47
+ part << " else {\n"
48
+ indent!
49
+ insert_into(code[1], part)
50
+ unindent!
51
+ part << indent_level << "}"
51
52
  end
52
53
 
53
54
  protected
@@ -55,7 +56,7 @@ module Liquidscript
55
56
  def _exec_context(code)
56
57
 
57
58
  unless code.locals.empty?
58
- "var #{code.locals.join(',')}; "
59
+ "\n#{indent_level}var #{code.locals.join(',')};\n"
59
60
  end
60
61
  end
61
62
  end
@@ -38,7 +38,7 @@ module Liquidscript
38
38
  prop << replace(code[1])
39
39
  end
40
40
 
41
- prop << "." << code[2].value
41
+ prop << "." << code[2]
42
42
  end
43
43
 
44
44
  def generate_class(code)
@@ -46,23 +46,29 @@ module Liquidscript
46
46
  class_name = code[1].value
47
47
 
48
48
  in_module(class_name) do |last_module|
49
- body.block <<-JS
49
+ body.block 7 - @indent, <<-JS
50
50
  #{class_name} = #{class_name} || function #{class_name}() {
51
51
  if(this.initialize) {
52
52
  this.initialize.apply(this, arguments);
53
53
  }
54
- };
54
+ }
55
55
  JS
56
56
 
57
- code[2].each do |part|
57
+ if code[2]
58
+ body.block 8 - @indent, <<-JS
59
+ #{class_name}.prototype.__proto__ = #{code[2].value};
60
+ JS
61
+ end
62
+
63
+ code[3].each do |part|
58
64
  k, v = part
59
65
  case k.type
60
66
  when :identifier
61
- body.block <<-JS
67
+ body.block 8 - @indent, <<-JS
62
68
  #{class_name}.prototype.#{k.value} = #{replace(v)};
63
69
  JS
64
- when :dstring
65
- body.block <<-JS
70
+ when :istring
71
+ body.block 8 - @indent, <<-JS
66
72
  #{class_name}.prototype[#{k.value}] = #{replace(v)};
67
73
  JS
68
74
  when :property
@@ -70,9 +76,8 @@ module Liquidscript
70
76
  raise InvalidCodeError.new(k[1].value)
71
77
  end
72
78
 
73
- body.block <<-JS
74
- #{class_name}.#{k[2].value} =
75
- #{replace(v)};
79
+ body.block 8 - @indent, <<-JS
80
+ #{class_name}.#{k[2]} = #{replace(v)};
76
81
  JS
77
82
  when :class
78
83
  body << generate_class(part)
@@ -82,8 +87,8 @@ module Liquidscript
82
87
  end
83
88
 
84
89
  if last_module
85
- body.block <<-JS
86
- #{last_module}.#{class_name} = #{class_name};
90
+ body.block 7, <<-JS
91
+ #{last_module}.#{class_name} = #{class_name}
87
92
  JS
88
93
  end
89
94
 
@@ -103,11 +108,11 @@ module Liquidscript
103
108
  k, v = part
104
109
  case k
105
110
  when :identifier
106
- body.block <<-JS
111
+ body.block 8, <<-JS
107
112
  #{module_name}.#{k.value} = #{replace(v)};
108
113
  JS
109
114
  when :dstring
110
- body.block <<-JS
115
+ body.block 8, <<-JS
111
116
  #{module_name}[#{k.value}] = #{replace(v)};
112
117
  JS
113
118
  when :class
@@ -118,8 +123,8 @@ module Liquidscript
118
123
  end
119
124
 
120
125
  if last_module
121
- body.block <<-JS
122
- #{last_module}.#{module_name} = #{module_name};
126
+ body.block 7, <<-JS
127
+ #{last_module}.#{module_name} = #{module_name}
123
128
  JS
124
129
  end
125
130
  end
@@ -43,6 +43,14 @@ module Liquidscript
43
43
  @code << Code.new(action, arguments)
44
44
  end
45
45
 
46
+ def <<(*v)
47
+ v.select { |p| p }.each do |part|
48
+ @code << part
49
+ end
50
+ end
51
+
52
+ alias_method :push, :<<
53
+
46
54
  # A list of all the local variables in the
47
55
  # current scope. Local variables are defined
48
56
  # as variables that were a) not passed in by
@@ -7,18 +7,20 @@ module Liquidscript
7
7
  key, value = context.find_matcher(scanner)
8
8
 
9
9
  if value.nil? && scanner.rest?
10
- error scanner
10
+ error scanner, context
11
11
  end
12
12
 
13
13
  normalize_action key, value, scanner if scanner.rest?
14
14
  end
15
15
 
16
- def lex(argument)
17
- context, body =
18
- if argument.is_a?(Hash)
19
- argument.to_a.first
16
+ def lex(*args)
17
+ args.flatten!
18
+ to_lex = args.pop
19
+
20
+ context, body = if to_lex.is_a? Hash
21
+ to_lex.to_a.first
20
22
  else
21
- argument
23
+ [to_lex, nil]
22
24
  end
23
25
 
24
26
  scanner = if body
@@ -29,7 +31,7 @@ module Liquidscript
29
31
  out = []
30
32
 
31
33
  context = find_context(context)
32
- instance_exec &context.init
34
+ instance_exec(*args, &context.init)
33
35
 
34
36
  while scanner.rest? && out.last != EXIT
35
37
  out << perform_with_context(context, scanner)
@@ -38,8 +40,9 @@ module Liquidscript
38
40
  out
39
41
  end
40
42
 
41
- def error(scanner = @scanner)
42
- raise SyntaxError, "Unexpected #{scanner.peek(2).inspect}" \
43
+ def error(scanner = @scanner, context = @context)
44
+ raise SyntaxError, "Unexpected " +
45
+ "#{scanner.matched}#{scanner.peek(2)}".inspect +
43
46
  " (line: #{line}, column: #{column})"
44
47
  end
45
48
 
@@ -68,7 +71,7 @@ module Liquidscript
68
71
  when Proc
69
72
  instance_exec(*body.match(key), &value)
70
73
  when Symbol
71
- lex value
74
+ lex(*body.match(key), value)
72
75
  when EXIT
73
76
  EXIT
74
77
  end
@@ -30,16 +30,23 @@ module Liquidscript
30
30
  new
31
31
  return
32
32
  typeof
33
+ throw
34
+ )
35
+
36
+ set :actions, %w(
37
+ break
38
+ continue
33
39
  )
34
40
 
35
41
  set :binops, %w(
36
- + - * / & | ^
42
+ + - * / ^
37
43
  << >> >>>
38
44
  == ===
39
45
  != !==
40
46
  > >=
41
47
  < <=
42
48
  && ||
49
+ & |
43
50
  instanceof
44
51
  or and
45
52
  )
@@ -59,43 +66,59 @@ module Liquidscript
59
66
  on("unless") { emit :unless }
60
67
  on("elsif") { emit :elsif }
61
68
  on("else") { emit :else }
69
+ on("for") { emit :for }
70
+ on("while") { emit :while }
71
+ on("try") { emit :try }
72
+ on("catch") { emit :catch }
73
+ on("finally") { emit :finally }
62
74
  on(:number) { |m| emit :number, m }
63
75
  on(:string) { |m| emit :sstring, m }
64
76
  on(:keywords) { |m| emit :keyword, m }
65
- on(:unops) { |m| emit :unop, m }
77
+ on(:actions) { |m| emit :action, m }
66
78
  on("->") { emit :arrow }
67
79
  on("=") { emit :equal }
68
- on("{") { emit :lbrack }
80
+ on("{") { emit :lbrace }
69
81
  on("(") { emit :lparen }
70
- on("[") { emit :lbrace }
71
- on("}") { emit :rbrack }
82
+ on("[") { emit :lbrack }
83
+ on("}") { emit :rbrace }
72
84
  on(")") { emit :rparen }
73
- on("]") { emit :rbrace }
85
+ on("]") { emit :rbrack }
74
86
  on(":") { emit :colon }
75
87
  on(".") { emit :prop }
76
88
  on(",") { emit :comma }
77
- on("\n") { line!; emit :newline}
89
+ on("\n") { line! }
90
+ on(%r{"} => :istring)
91
+ on(%r{<<([A-Z]+)}) do |_, s|
92
+ emit :heredoc_ref, s
93
+ @lexes << [:heredoc, s]
94
+ end
95
+ on(%r{<<-([A-Z]+)}) do |_, s|
96
+ emit :iheredoc_ref, s
97
+ @lexes << [:iheredoc, s]
98
+ end
99
+ on(%r{/(.*?)/([a-z]*)}) { |_, m, b|
100
+ emit :regex, [m, b]
101
+ }
102
+ on("///" => :block_regex)
78
103
  on(:binops) { |m| emit :binop, m }
104
+ on(:unops) { |m| emit :unop, m }
79
105
  on(:identifier) { |m| emit :identifier, m }
80
106
 
81
- on(/\"/ => :istring)
82
- on(/#.*?\n/) { }
83
- on(/[\s]/) { }
84
- on(:_) { error }
107
+ on(%r{#.*?\n}) { }
108
+ on(%r{\s}) { }
109
+ on(:_) { |m| error }
85
110
  end
86
111
 
87
112
  context :istring do
88
- init do
89
- @buffer = []
90
- end
113
+ init { @buffer = [] }
91
114
 
92
- on(/\\"/) { |m| @buffer << m }
93
- on(/"/) do
115
+ on('\\"') { |m| @buffer << m }
116
+ on('"') do
94
117
  emit :istring, @buffer.join
95
118
  exit
96
119
  end
97
120
 
98
- on(/\#\{(.*?)\}/) do |_, b|
121
+ on(%r{\#\{(.*?)\}}) do |_, b|
99
122
  emit :istring_begin, @buffer.join
100
123
  lex :main => b
101
124
  @buffer = []
@@ -103,17 +126,72 @@ module Liquidscript
103
126
 
104
127
  on(:_) { |m| @buffer << m }
105
128
  end
129
+
130
+ context :heredoc do
131
+ init { @buffer = [] }
132
+
133
+ on(%r{\n\s*([A-Z]+)\s*\n}) do |_, s|
134
+ if @start == s
135
+ emit :heredoc, @buffer.join
136
+ exit
137
+ else
138
+ @buffer << _
139
+ end
140
+ end
141
+
142
+ on(:_) { |m| @buffer << m }
143
+ end
144
+
145
+ context :iheredoc do
146
+ init { @buffer = [] }
147
+
148
+ on(%r{\n\s*([A-Z]+)\s*\n}) do |_, s|
149
+ if @start == s
150
+ emit :iheredoc, @buffer.join
151
+ @start = nil
152
+ exit
153
+ else
154
+ @buffer << _
155
+ end
156
+ end
157
+
158
+ on(%r{\#\{(.*?)\}}) do |_, b|
159
+ emit :iheredoc_begin, @buffer.join
160
+ lex :main => b
161
+ @buffer = []
162
+ end
163
+
164
+ on(:_) { |m| @buffer << m }
165
+ end
166
+
167
+ context :block_regex do
168
+ init { @buffer = [] }
169
+
170
+ on(%r{///([a-z]*)}) do |_, m|
171
+ emit :regex, [@buffer.join, m]
172
+ exit
173
+ end
174
+ on(%r{#.*?\n}) { }
175
+ on("\n") { }
176
+ on(:_) { |m| @buffer << m }
177
+ end
106
178
  end
107
179
 
108
180
  def initialize(*)
109
181
  @line = 1
110
182
  @cstart = 0
183
+ @lexes = []
111
184
  super
112
185
  end
113
186
 
114
187
  def line!
115
188
  @line += 1
116
189
  @cstart = @scanner.pos
190
+ while @lexes.any?
191
+ type, @start = @lexes.shift
192
+
193
+ lex type
194
+ end
117
195
  end
118
196
 
119
197
  def line