liquidscript 0.0.1

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