liquidscript 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +11 -0
  6. data/Guardfile +9 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +59 -0
  9. data/Rakefile +25 -0
  10. data/lib/liquidscript.rb +11 -0
  11. data/lib/liquidscript/buffer.rb +34 -0
  12. data/lib/liquidscript/compiler.rb +9 -0
  13. data/lib/liquidscript/compiler/base.rb +101 -0
  14. data/lib/liquidscript/compiler/base/action.rb +39 -0
  15. data/lib/liquidscript/compiler/base/blank.rb +24 -0
  16. data/lib/liquidscript/compiler/base/callable.rb +51 -0
  17. data/lib/liquidscript/compiler/base/helpers.rb +207 -0
  18. data/lib/liquidscript/compiler/icr.rb +40 -0
  19. data/lib/liquidscript/compiler/icr/classes.rb +59 -0
  20. data/lib/liquidscript/compiler/icr/expressions.rb +94 -0
  21. data/lib/liquidscript/compiler/icr/functions.rb +42 -0
  22. data/lib/liquidscript/compiler/icr/helpers.rb +20 -0
  23. data/lib/liquidscript/compiler/icr/literals.rb +106 -0
  24. data/lib/liquidscript/errors.rb +51 -0
  25. data/lib/liquidscript/generator.rb +11 -0
  26. data/lib/liquidscript/generator/base.rb +25 -0
  27. data/lib/liquidscript/generator/base/dsl.rb +19 -0
  28. data/lib/liquidscript/generator/base/replacements.rb +33 -0
  29. data/lib/liquidscript/generator/context.rb +7 -0
  30. data/lib/liquidscript/generator/javascript.rb +37 -0
  31. data/lib/liquidscript/generator/javascript/literals.rb +63 -0
  32. data/lib/liquidscript/generator/javascript/metas.rb +41 -0
  33. data/lib/liquidscript/generator/javascript/objects.rb +137 -0
  34. data/lib/liquidscript/icr.rb +18 -0
  35. data/lib/liquidscript/icr/code.rb +68 -0
  36. data/lib/liquidscript/icr/context.rb +94 -0
  37. data/lib/liquidscript/icr/representable.rb +39 -0
  38. data/lib/liquidscript/icr/set.rb +147 -0
  39. data/lib/liquidscript/icr/sexp.rb +41 -0
  40. data/lib/liquidscript/icr/variable.rb +68 -0
  41. data/lib/liquidscript/scanner.rb +40 -0
  42. data/lib/liquidscript/scanner/lexer.rl +106 -0
  43. data/lib/liquidscript/scanner/token.rb +37 -0
  44. data/lib/liquidscript/template.rb +16 -0
  45. data/lib/liquidscript/version.rb +5 -0
  46. data/liquidscript.gemspec +27 -0
  47. data/spec/fixtures/class.compile.yml +26 -0
  48. data/spec/fixtures/class.generate.yml +31 -0
  49. data/spec/fixtures/combination.generate.yml +33 -0
  50. data/spec/fixtures/complex.generate.yml +20 -0
  51. data/spec/fixtures/expression.generate.yml +4 -0
  52. data/spec/fixtures/function.generate.yml +11 -0
  53. data/spec/fixtures/get.generate.yml +5 -0
  54. data/spec/fixtures/literals.generate.yml +8 -0
  55. data/spec/fixtures/main.compile.yml +32 -0
  56. data/spec/fixtures/set.generate.yml +4 -0
  57. data/spec/fixtures/string.generate.yml +6 -0
  58. data/spec/lib/liquidscript/buffer_spec.rb +14 -0
  59. data/spec/lib/liquidscript/compiler/icr_spec.rb +139 -0
  60. data/spec/lib/liquidscript/generator/javascript_spec.rb +15 -0
  61. data/spec/lib/liquidscript/icr/code_spec.rb +0 -0
  62. data/spec/lib/liquidscript/icr/context_spec.rb +36 -0
  63. data/spec/lib/liquidscript/icr/set_spec.rb +59 -0
  64. data/spec/lib/liquidscript/scanner/lexer_spec.rb +58 -0
  65. data/spec/lib/liquidscript/scanner/token_spec.rb +0 -0
  66. data/spec/lib/liquidscript/scanner_spec.rb +21 -0
  67. data/spec/spec_helper.rb +30 -0
  68. data/spec/support/helpers/lexer_helper.rb +5 -0
  69. data/spec/support/matchers/be_token.rb +9 -0
  70. data/spec/support/matchers/compile.rb +41 -0
  71. data/spec/support/matchers/generate.rb +46 -0
  72. metadata +210 -0
@@ -0,0 +1,207 @@
1
+ module Liquidscript
2
+ module Compiler
3
+ class Base
4
+ module Helpers
5
+
6
+ module ClassMethods
7
+ def allowable
8
+ @_allowable ||= {}
9
+ end
10
+
11
+ def always(type)
12
+ case type
13
+ when Hash
14
+ allowable.merge!(type)
15
+ when Symbol
16
+ allowable[type] = type
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.included(base)
22
+ base.extend ClassMethods
23
+ end
24
+
25
+ # Normalizes an action for the hash passed to {#expect}. If
26
+ # a block is given, it returns that block. If the argument is
27
+ # a proc, it returns that proc. If none of those conditions are
28
+ # met, it returns the {Action}.
29
+ #
30
+ # @yield nothing.
31
+ # @param act [Proc, nil] the proc to return, if it is a proc.
32
+ # @return [Proc, Action]
33
+ def action(act = nil)
34
+ if block_given?
35
+ Proc.new
36
+ elsif act.is_a? Proc
37
+ act
38
+ else
39
+ @action
40
+ end
41
+ end
42
+
43
+ # Performs a loop while the yield returns true. This
44
+ # overwrites the core loop on purpose.
45
+ #
46
+ # @yieldreturn [Boolean] whether or not to continue looping.
47
+ # @return [void]
48
+ def loop
49
+ result = true
50
+
51
+ while result
52
+ result = yield
53
+ end
54
+ end
55
+
56
+ # Shift a token over. The given types can be any types. If
57
+ # the next token's type doesn't match any of the types, then
58
+ # it will raise an error. In order to shift any token, use
59
+ # the special type `:_`.
60
+ #
61
+ # @param types [Symbol] the token types to look for.
62
+ # @return [#type] the token.
63
+ def shift(*types)
64
+ expect types => action.shift
65
+ end
66
+
67
+ # Shifts a token if its one of the given types; if it's not,
68
+ # it returns the value of {#scanner_nil}.
69
+ #
70
+ # @param types [Symbol] the token types to look for.
71
+ # @return [#type] the token.
72
+ def maybe(*types)
73
+ expect types => action.shift, :_ => action { scanner_nil }
74
+ end
75
+
76
+ # Checks to see if the next token is of any of the given
77
+ # types. Note that the special type `:_` does not work here.
78
+ #
79
+ # @param types [Symbol] the token types to match.
80
+ # @return [Boolean] whether or not the next token's type
81
+ # matches any of the given types.
82
+ def peek?(*types)
83
+ types.any? { |type| peek.type == type }
84
+ end
85
+
86
+ # The meat and potatos of the compiler. This maps actions to
87
+ # tokens. In its basic form, it is passed a hash, with the
88
+ # keys being token types, and the values the corresponding
89
+ # actions to take. It can be passed individual types, from
90
+ # which it'll assume the method name (normally,
91
+ # `compile_<type>`); or, you can pass it a symbol key, which
92
+ # is the last part of the method name (i.e., not including
93
+ # `compile_`). It will check the next token, and look for the
94
+ # correct action; if the next token doesn't match any of the
95
+ # given keys, it checks for the special type `:_` (which is
96
+ # basically a catch-all), and if it still doesn't get it, it
97
+ # raises an {UnexpectedError}. From there, it calls the
98
+ # corresponding block.
99
+ #
100
+ # If the block or method accepts one argument, it {#pop}s the
101
+ # token, and passes it in as an argument. If it accepts no
102
+ # arguments, it doesn't.
103
+ #
104
+ # @example
105
+ # # If the next token has type `:test`, it will output
106
+ # # "hello!" If the next token has any other type, it
107
+ # # raises an error.
108
+ # expect :test => action { puts "hello!" }
109
+ # @example
110
+ # # If the next token has the type `:number`, it calls
111
+ # # the method `compile_number`; if the next token has
112
+ # # the type `:dstring`, it calls the method
113
+ # # `compile_string`.
114
+ # expect :number, :dstring => :string
115
+ # @example
116
+ # # Just pops the comma token.
117
+ # expect :comma => action.shift
118
+ # @example
119
+ # # When given a hash with one of the keys as an array, each
120
+ # # element of that array is treated as a token type, and
121
+ # # that token type is mapped to the value as an action.
122
+ # expect [:identifier, :dstring] => action.end_loop
123
+ # @example
124
+ # # When given `:_` as an argument, it'll map the next token
125
+ # # to its corresponding compile function.
126
+ # expect :_
127
+ # # If the next token's type is `:identifier`, it will call
128
+ # # `compile_identifier`.
129
+ # @param *args [Array<Symbol, Hash<Symbol, Object>>]
130
+ # @raise [UnexpectedError] if the next token's type didn't
131
+ # match any of the given types, and the `:_` type wasn't
132
+ # given.
133
+ # @return [Object] the result of the block/method call.
134
+ def expect(*args)
135
+ hash = normalize_arguments(args)
136
+ allowable = false
137
+
138
+ block = hash.fetch(peek.type) do
139
+ hash.fetch(:_) do
140
+ allowable = true
141
+ self.allowable.fetch(peek.type)
142
+ end
143
+ end
144
+
145
+ out = if block.arity == 1
146
+ block.call pop
147
+ else
148
+ block.call
149
+ end
150
+
151
+ if allowable
152
+ expect(*args)
153
+ else
154
+ out
155
+ end
156
+
157
+ rescue KeyError
158
+ raise UnexpectedError.new(hash.keys, peek)
159
+ end
160
+
161
+ protected
162
+
163
+ def allowable
164
+ @_allowable ||= begin
165
+ self.class.allowable.to_a.inject({}) do |m, (k, v)|
166
+ m.merge! k => Callable.new(self, v)
167
+ end
168
+ end
169
+ end
170
+
171
+ # Normalizes the arguments for {#expect}. Turns all of the
172
+ # values into {Callable}s, and everything that's not a hash
173
+ # is merged into the hash.
174
+ #
175
+ # @param args [Array<Hash, Symbol>]
176
+ # @return [Hash]
177
+ def normalize_arguments(args)
178
+ hash = if args.last.is_a? Hash
179
+ args.pop
180
+ else
181
+ {}
182
+ end
183
+
184
+ args.inject(hash) do |h, a|
185
+ h.merge! a => if a == :_
186
+ peek.type
187
+ else
188
+ a
189
+ end
190
+ end
191
+
192
+ hash.inject({}) do |out, (key, value)|
193
+ c = Callable.new(self, value)
194
+
195
+ if key.is_a? Array
196
+ key.each { |k| out[k] = c }
197
+ else
198
+ out[key] = c
199
+ end
200
+
201
+ out
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,40 @@
1
+ require "liquidscript/compiler/icr/expressions"
2
+ require "liquidscript/compiler/icr/functions"
3
+ require "liquidscript/compiler/icr/literals"
4
+ require "liquidscript/compiler/icr/classes"
5
+ require "liquidscript/compiler/icr/helpers"
6
+
7
+ module Liquidscript
8
+ module Compiler
9
+ class ICR < Base
10
+
11
+ include Expressions
12
+ include Functions
13
+ include Literals
14
+ include Classes
15
+ include Helpers
16
+
17
+ always :keyword
18
+
19
+ # (see Base#reset!)
20
+ def reset!
21
+ @top = Liquidscript::ICR::Set.new
22
+ @top.context = Liquidscript::ICR::Context.new
23
+ @set = [@top]
24
+ super
25
+ end
26
+
27
+ # (see Base#top)
28
+ def top
29
+ @set.last
30
+ end
31
+
32
+ # Sets the starting point for compiliation.
33
+ #
34
+ # @return [ICR::Code]
35
+ def compile_start
36
+ expect :class, :module, :_ => :expression
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ module Liquidscript
2
+ module Compiler
3
+ class ICR
4
+ module Classes
5
+
6
+ def compile_class
7
+ shift :class
8
+ name = shift :identifier
9
+ body = _compile_class_body
10
+
11
+ set name
12
+ code :class, name, body
13
+ end
14
+
15
+ def compile_module
16
+ shift :module
17
+ name = shift :identifier
18
+ body = _compile_class_body(true)
19
+
20
+ set name
21
+ code :module, name, body
22
+ end
23
+
24
+ def _compile_class_body(mod = false)
25
+ shift :lbrack
26
+
27
+ components = []
28
+
29
+ compile_object = action do
30
+ components << [
31
+ _compile_class_body_key(mod),
32
+ compile_expression
33
+ ]
34
+ end
35
+
36
+ loop do
37
+ expect :rbrack => action.end_loop,
38
+ :comma => action.shift,
39
+ :module => action { components << compile_module },
40
+ :class => action { components << compile_class },
41
+ [:identifier, :dstring] => compile_object
42
+ end
43
+
44
+ components
45
+ end
46
+
47
+ def _compile_class_body_key(mod)
48
+ item = shift :identifier, :dstring
49
+
50
+ item = compile_property(item) if item.type == :identifier &&
51
+ peek?(:prop) && mod
52
+
53
+ shift :colon
54
+ item
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,94 @@
1
+ module Liquidscript
2
+ module Compiler
3
+ class ICR < Base
4
+ module Expressions
5
+
6
+ # Compiles an expression. This is primarily used in a general
7
+ # context, such that anything can be returned.
8
+ #
9
+ # @return [ICR::Code]
10
+ def compile_expression
11
+ expect :number, :identifier,
12
+ :dstring, :lparen,
13
+ :sstring, :keyword,
14
+ :lbrack => :object,
15
+ :lbrace => :array,
16
+ :arrow => :function
17
+ end
18
+
19
+ # Handles an assignment of the form `identifier = expression`,
20
+ # with the argument being the identifier, and the position of
21
+ # the compiler being after it.
22
+ #
23
+ # @return [ICR::Code]
24
+ def compile_assignment(identifier)
25
+ shift :equal
26
+ value = compile_expression
27
+
28
+ if identifier.type == :identifier
29
+ variable = set(identifier)
30
+ variable.value = value
31
+ else
32
+ variable = identifier
33
+ end
34
+
35
+ code :set, variable, value
36
+ end
37
+
38
+ # We don't know, at this point, whether this is a function
39
+ # declaration, or an expression; if it's a function declaration,
40
+ # the contents of the lparen can only be commas and identifiers;
41
+ # if it's an expression, it can have anything but commas in it.
42
+ #
43
+ # @return [ICR::Code]
44
+ def compile_lparen
45
+ shift :lparen
46
+ maybe_func = 1
47
+ components = []
48
+
49
+ unless peek?(:identifier, :rparen)
50
+ maybe_func = 0
51
+ end
52
+
53
+ expression = action do
54
+ maybe_func = 0
55
+ components << compile_expression
56
+ end
57
+
58
+ loop do
59
+ case maybe_func
60
+ when 0
61
+ expect :rparen => action.end_loop,
62
+ :_ => expression
63
+ when 1
64
+ expect :rparen => action.end_loop,
65
+ :comma => action { maybe_func = 2 },
66
+ :identifier => action { |i| components << i },
67
+ :_ => expression
68
+ when 2
69
+ expect :rparen => action.end_loop,
70
+ :comma => action.shift,
71
+ :identifier => action { |i| components << i }
72
+ end
73
+ end
74
+
75
+ func_decl = (maybe_func == 1 && peek?(:arrow)) ||
76
+ (maybe_func == 2)
77
+
78
+ if func_decl
79
+ compile_function_with_parameters(components)
80
+ else
81
+ code(:expression, components.map do |c|
82
+ if c.is_a?(Scanner::Token)
83
+ compile_identifier(c)
84
+ else
85
+ c
86
+ end
87
+ end)
88
+ end
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,42 @@
1
+ module Liquidscript
2
+ module Compiler
3
+ class ICR < Base
4
+ module Functions
5
+
6
+ def compile_property(prop)
7
+ shift :prop
8
+
9
+ ref = if prop.type == :identifier
10
+ ref(prop)
11
+ else
12
+ prop
13
+ end
14
+
15
+ property = action do |ident|
16
+ code :property, ref, ident
17
+ end
18
+
19
+ code = expect :identifier => property
20
+
21
+ expect :lparen => action { compile_call(code) },
22
+ :equal => action { compile_assignment(code) },
23
+ :prop => action { compile_property(code) },
24
+ :_ => action { code }
25
+ end
26
+
27
+ def compile_call(subject)
28
+ shift :lparen
29
+ arguments = []
30
+
31
+ loop do
32
+ expect :comma => action.shift,
33
+ :rparen => action.end_loop,
34
+ :_ => action { arguments << compile_expression }
35
+ end
36
+
37
+ code :call, subject, *arguments
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ module Liquidscript
2
+ module Compiler
3
+ class ICR < Base
4
+ module Helpers
5
+
6
+ def ref(literal)
7
+ top.context.get(literal.value.intern)
8
+ end
9
+
10
+ def set(literal)
11
+ top.context.set(literal.value.intern)
12
+ end
13
+
14
+ def code(type, *args)
15
+ Liquidscript::ICR::Code.new type, *args
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end