liquidscript 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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