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,18 @@
1
+ require "liquidscript/icr/representable"
2
+ require "liquidscript/icr/code"
3
+ require "liquidscript/icr/set"
4
+ require "liquidscript/icr/sexp"
5
+ require "liquidscript/icr/context"
6
+ require "liquidscript/icr/variable"
7
+
8
+ module Liquidscript
9
+
10
+ # The ICR (rather, Intermediate Code Representation)
11
+ # is a method used to transform compiled code into
12
+ # the corresponding javascript code. It can be used
13
+ # to optimize the code first, ensuring that the
14
+ # resultant code is fast. The point here is to
15
+ # represent the code, and make it seem nice.
16
+ module ICR
17
+ end
18
+ end
@@ -0,0 +1,68 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ # An individual code point. This is normally in
5
+ # a set. A code will have an action, and arguments
6
+ # that accompany that action. The arguments list
7
+ # can be however long.
8
+ class Code
9
+
10
+ # The action that this code is associated with.
11
+ # This should be a symbol.
12
+ #
13
+ # @return [Symbol]
14
+ attr_reader :action
15
+
16
+ # The arguments that are used for this action.
17
+ # This is an array.
18
+ #
19
+ # @return [Array]
20
+ attr_reader :arguments
21
+
22
+ alias_method :type, :action
23
+
24
+ include Representable
25
+
26
+ # Initializes the code. It takes an action and
27
+ # an argument as its arguments. The action
28
+ # should not change from this point forward.
29
+ #
30
+ # @param action [Symbol]
31
+ # @param arguments [Array]
32
+ def initialize(action, *arguments)
33
+ @action = action
34
+ @arguments = arguments
35
+ end
36
+
37
+ # Turns the code into an array, containing the
38
+ # action and the arguments. Note that changing
39
+ # this array will not change the code.
40
+ #
41
+ # @return [Array]
42
+ def to_a
43
+ [@action, *@arguments]
44
+ end
45
+
46
+ # If we don't respond to it, the @arguments array
47
+ # might. Ask them if they do, and if they don't,
48
+ # respond accordingly.
49
+ #
50
+ # @param method [Symbol] the method to check.
51
+ # @param include_private [Boolean] whether or not to
52
+ # include private methods.
53
+ # @return [Boolean]
54
+ def respond_to_missing?(method, include_private = false)
55
+ @arguments.respond_to?(method)
56
+ end
57
+
58
+ # Send the method to @arguments if it doesn't
59
+ # exist here.
60
+ #
61
+ # @return [Object]
62
+ def method_missing(method, *args, &block)
63
+ @arguments.public_send(method, *args, &block)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,94 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ # Handles variables within blocks. Each variable will get a
5
+ # reference in each context. When retrieving the value of
6
+ # a variable, if the variable was not introduced in the scope,
7
+ # it will look to its parent for the value of the variable.
8
+ # When setting the value of a variable, a new variable is
9
+ # forcibly created.
10
+ class Context
11
+
12
+ # The variables that are allowed to be used as a global scope,
13
+ # i.e. used in a `get` context without a previous `set`.
14
+ DEFAULT_ALLOWED_VARIABLES = [
15
+ :window, :global, :exports, :console, :this
16
+ ]
17
+
18
+ # The parent of the current context.
19
+ #
20
+ # @return [Parent]
21
+ attr_accessor :parent
22
+
23
+ # The variables that are a part of this context.
24
+ #
25
+ # @return [Hash<Symbol, Variable>]
26
+ attr_reader :variables
27
+
28
+ # The variables that are allowed to be used as a global scope,
29
+ # i.e. used in a `get` context without a previous set.
30
+ #
31
+ # @see [DEFAULT_ALLOWED_VARIABLES]
32
+ # @return [Array<Symbol>]
33
+ attr_reader :allowed_variables
34
+
35
+ include Representable
36
+
37
+ # Initializes the context.
38
+ def initialize
39
+ @variables = {}
40
+ @allowed_variables = [DEFAULT_ALLOWED_VARIABLES].flatten
41
+ end
42
+
43
+ # Returns a variable reference. If checks the local variables
44
+ # first; if it doesn't exist there, then if the type is `:get`,
45
+ # checks the parent; otherwise, sets the value of the variable.
46
+ # If there is no parent and the type is `:get`, it raises an
47
+ # {InvalidReferenceError}.
48
+ #
49
+ # @param name [Symbol] the name of the variable.
50
+ # @param type [Symbol] the type of use. Should be `:get` or
51
+ # `:set`.
52
+ # @return [Variable]
53
+ def variable(name, type)
54
+ @variables.fetch(name) do
55
+ if type == :set
56
+ @variables[name] = Variable.new(self, name)
57
+ elsif allowed_variables.include?(name)
58
+ Variable.new(self, name)
59
+ elsif type == :get && parent
60
+ parent.get(name)
61
+ else
62
+ raise InvalidReferenceError.new(name)
63
+ end
64
+ end
65
+ end
66
+
67
+ # All of the parameter variables.
68
+ #
69
+ # @return [Array<Variable>]
70
+ def parameters
71
+ @variables.values.select(&:parameter?)
72
+ end
73
+
74
+ # (see #variable).
75
+ #
76
+ # Passes `:get` as type.
77
+ def get(name)
78
+ variable(name, :get)
79
+ end
80
+
81
+ # (see #variable).
82
+ #
83
+ # Passes `:set` as type.
84
+ def set(name)
85
+ variable(name, :set)
86
+ end
87
+
88
+ def to_a
89
+ @variables.keys
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,39 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ # Used to show that a specific element of the ICR is representable
5
+ # as an array. It will forward the methods #to_s and #inspect to
6
+ # the #to_a method, expecting the including module to define it.
7
+ module Representable
8
+
9
+ extend Forwardable
10
+ include Comparable
11
+
12
+ def_delegators :to_a, :to_s, :inspect, :[], :each, :'<=>'
13
+
14
+
15
+ def to_ary
16
+ to_a
17
+ end
18
+
19
+ def to_yaml
20
+ to_a!.to_yaml
21
+ end
22
+
23
+ def to_a!
24
+ do_map = proc do |e|
25
+ if e.is_a?(Representable)
26
+ e.to_a!
27
+ elsif e.is_a? Array
28
+ e.map(&do_map)
29
+ else
30
+ e
31
+ end
32
+ end
33
+
34
+ to_a.map(&do_map)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,147 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ # Represents a set of instruction codes. Can contain
5
+ # metadata about the set, like whether or not it can
6
+ # be inlined, if a new context needs to be applied,
7
+ # etc.
8
+ class Set < Code
9
+
10
+ # The metadata that is applied to the set.
11
+ #
12
+ # @return [Hash]
13
+ attr_reader :metadata
14
+
15
+ include Representable
16
+
17
+ # Initialize the set.
18
+ def initialize
19
+ @metadata = {}
20
+ @code = []
21
+ @action = :exec
22
+ end
23
+
24
+ #
25
+ def context
26
+ @metadata.fetch(:context) do
27
+ @metadata.fetch(:parent).context
28
+ end
29
+ end
30
+
31
+ #
32
+ def context=(new_context)
33
+ @metadata[:context] = new_context
34
+ end
35
+
36
+ # Adds a code to the code list. This is just a
37
+ # convienince method.
38
+ #
39
+ # @param action [Symbol] a symbol representing
40
+ # the action.
41
+ # @param arguments the arguments for the code.
42
+ def add(action, *arguments)
43
+ @code << Code.new(action, arguments)
44
+ end
45
+
46
+ # A list of all the local variables in the
47
+ # current scope. Local variables are defined
48
+ # as variables that were a) not passed in by
49
+ # function execution as arguments and b) are
50
+ # set within the current context.
51
+ #
52
+ # @return [Array<Symbol>]
53
+ def locals
54
+ variables - parameters
55
+ end
56
+
57
+ # A list of components (or arguments) that are
58
+ # in the current scope. Defined as variables
59
+ # that were passed in by function execution as
60
+ # arguments.
61
+ #
62
+ # @return [Array<Symbol>]
63
+ def parameters
64
+ context.parameters.map(&:name)
65
+ end
66
+
67
+ # A list of _all_ variables in the current
68
+ # scope.
69
+ #
70
+ # @return [Array<Symbol>]
71
+ def variables
72
+ context.variables.keys - context.allowed_variables
73
+ end
74
+
75
+ # Turns the set into an array. Includes the
76
+ # metadata information and the actual internal
77
+ # array.
78
+ # Note that this is _not_ the array used in
79
+ # {#method_missing} - that actually operates on
80
+ # the internal array.
81
+ #
82
+ # @return [Array]
83
+ def to_a
84
+ [
85
+ @action,
86
+ *@metadata.to_a.map { |(m, i)| [:"_#{m}", i] },
87
+ *@code
88
+ ]
89
+ end
90
+
91
+ # Outputs the codes in this set.
92
+ #
93
+ # @return [Array<Code>]
94
+ def codes
95
+ @code
96
+ end
97
+
98
+ # Access either the metadata or the codes. If
99
+ # the accessor is a Symbol, it access the metadata;
100
+ # if it the accessor is a Numeric, it access the
101
+ # codes.
102
+ #
103
+ # @param key [Symbol, Numeric] the key.
104
+ # @return [Object]
105
+ def [](key)
106
+ if key.is_a? Numeric
107
+ @code[key]
108
+ else
109
+ @metadata[key]
110
+ end
111
+ end
112
+
113
+ # Sets something from the metadata. Unlike the
114
+ # accessor, it does not distinguish between
115
+ # Numeric and Symbol keys.
116
+ #
117
+ # @param key [Object] the key.
118
+ # @param value [Object] the value.
119
+ # @return [Object]
120
+ def []=(key, value)
121
+ @metadata[key] = value
122
+ end
123
+
124
+ # Tells ruby that we respond to some methods.
125
+ # Passes the method name to the internal
126
+ # array, asking if it responds to it.
127
+ #
128
+ # @param method [Symbol] the method to check.
129
+ # @param include_private [Boolean] whether or not
130
+ # to include private methods.
131
+ # @return [Boolean] whether or not we respond
132
+ # to that method.
133
+ def respond_to_missing?(method, include_private = false)
134
+ @code.respond_to?(method, include_private)
135
+ end
136
+
137
+ # For methods that we don't respond to, send
138
+ # them to the interal array.
139
+ #
140
+ # @return [Object]
141
+ def method_missing(method, *args, &block)
142
+ @code.public_send(method, *args, &block)
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,41 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ class ::Array; def to_sexp; Sexp.new(self).output; end; end
5
+
6
+ # @private
7
+ class Sexp
8
+
9
+ def initialize(compiler)
10
+ @compiler = compiler
11
+ @depth = 0
12
+ end
13
+
14
+ def output
15
+ out(@compiler).strip
16
+ end
17
+
18
+ private
19
+
20
+ def out(v)
21
+ if v.is_a?(Representable) || v.is_a?(Array)
22
+ @depth += 1
23
+ body = ["\n", " " * @depth, "(",
24
+ v.to_a.map {|d| out d }.join(' '),
25
+ ")"].join
26
+ @depth -= 1
27
+ body
28
+ else
29
+ body = v.to_s.gsub(/\"/, "\\\"")
30
+
31
+ if body.include? " "
32
+ "\"#{body}\""
33
+ else
34
+ body
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,68 @@
1
+ module Liquidscript
2
+ module ICR
3
+
4
+ # Represents a variable. It can hold a value, but it will not
5
+ # be used to reconstruct the variable itself.
6
+ class Variable
7
+
8
+ # The context in which this variable exists. The variable
9
+ # should exist in one and only one context, but can be used
10
+ # in contexts other than its own.
11
+ #
12
+ # @return [Context]
13
+ attr_reader :context
14
+
15
+ # The name of the variable that this represents. This will
16
+ # be used in both ICR and in the final output.
17
+ #
18
+ # @return [Symbol]
19
+ attr_reader :name
20
+
21
+ # The set that describes the value of this variable. The
22
+ # set just so happens to describe the value of the variable;
23
+ # it is not used to define the variable's value.
24
+ #
25
+ # @return [Code]
26
+ attr_accessor :value
27
+
28
+ include Representable
29
+
30
+ def initialize(context, name)
31
+ @context = context
32
+ @name = name
33
+ @value = nil
34
+ end
35
+
36
+ # Make this class compatible with the #type based system.
37
+ #
38
+ # @return [Symbol]
39
+ def type
40
+ :variable
41
+ end
42
+
43
+ # Marks this variable as an argument to a function. This is to
44
+ # let the context know not to show it in certain cases.
45
+ #
46
+ # @return [self]
47
+ def parameter!
48
+ @parameter = true
49
+ self
50
+ end
51
+
52
+ # Whether or not the variable is an argument to a function.
53
+ #
54
+ # @see {#parameter!}
55
+ # @return [Boolean]
56
+ def parameter?
57
+ @parameter
58
+ end
59
+
60
+ # Turns the variable into an array.
61
+ #
62
+ # @return [Array<(Symbol, Symbol)>]
63
+ def to_a
64
+ [:_variable, @name]
65
+ end
66
+ end
67
+ end
68
+ end