liquidscript 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +11 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +59 -0
- data/Rakefile +25 -0
- data/lib/liquidscript.rb +11 -0
- data/lib/liquidscript/buffer.rb +34 -0
- data/lib/liquidscript/compiler.rb +9 -0
- data/lib/liquidscript/compiler/base.rb +101 -0
- data/lib/liquidscript/compiler/base/action.rb +39 -0
- data/lib/liquidscript/compiler/base/blank.rb +24 -0
- data/lib/liquidscript/compiler/base/callable.rb +51 -0
- data/lib/liquidscript/compiler/base/helpers.rb +207 -0
- data/lib/liquidscript/compiler/icr.rb +40 -0
- data/lib/liquidscript/compiler/icr/classes.rb +59 -0
- data/lib/liquidscript/compiler/icr/expressions.rb +94 -0
- data/lib/liquidscript/compiler/icr/functions.rb +42 -0
- data/lib/liquidscript/compiler/icr/helpers.rb +20 -0
- data/lib/liquidscript/compiler/icr/literals.rb +106 -0
- data/lib/liquidscript/errors.rb +51 -0
- data/lib/liquidscript/generator.rb +11 -0
- data/lib/liquidscript/generator/base.rb +25 -0
- data/lib/liquidscript/generator/base/dsl.rb +19 -0
- data/lib/liquidscript/generator/base/replacements.rb +33 -0
- data/lib/liquidscript/generator/context.rb +7 -0
- data/lib/liquidscript/generator/javascript.rb +37 -0
- data/lib/liquidscript/generator/javascript/literals.rb +63 -0
- data/lib/liquidscript/generator/javascript/metas.rb +41 -0
- data/lib/liquidscript/generator/javascript/objects.rb +137 -0
- data/lib/liquidscript/icr.rb +18 -0
- data/lib/liquidscript/icr/code.rb +68 -0
- data/lib/liquidscript/icr/context.rb +94 -0
- data/lib/liquidscript/icr/representable.rb +39 -0
- data/lib/liquidscript/icr/set.rb +147 -0
- data/lib/liquidscript/icr/sexp.rb +41 -0
- data/lib/liquidscript/icr/variable.rb +68 -0
- data/lib/liquidscript/scanner.rb +40 -0
- data/lib/liquidscript/scanner/lexer.rl +106 -0
- data/lib/liquidscript/scanner/token.rb +37 -0
- data/lib/liquidscript/template.rb +16 -0
- data/lib/liquidscript/version.rb +5 -0
- data/liquidscript.gemspec +27 -0
- data/spec/fixtures/class.compile.yml +26 -0
- data/spec/fixtures/class.generate.yml +31 -0
- data/spec/fixtures/combination.generate.yml +33 -0
- data/spec/fixtures/complex.generate.yml +20 -0
- data/spec/fixtures/expression.generate.yml +4 -0
- data/spec/fixtures/function.generate.yml +11 -0
- data/spec/fixtures/get.generate.yml +5 -0
- data/spec/fixtures/literals.generate.yml +8 -0
- data/spec/fixtures/main.compile.yml +32 -0
- data/spec/fixtures/set.generate.yml +4 -0
- data/spec/fixtures/string.generate.yml +6 -0
- data/spec/lib/liquidscript/buffer_spec.rb +14 -0
- data/spec/lib/liquidscript/compiler/icr_spec.rb +139 -0
- data/spec/lib/liquidscript/generator/javascript_spec.rb +15 -0
- data/spec/lib/liquidscript/icr/code_spec.rb +0 -0
- data/spec/lib/liquidscript/icr/context_spec.rb +36 -0
- data/spec/lib/liquidscript/icr/set_spec.rb +59 -0
- data/spec/lib/liquidscript/scanner/lexer_spec.rb +58 -0
- data/spec/lib/liquidscript/scanner/token_spec.rb +0 -0
- data/spec/lib/liquidscript/scanner_spec.rb +21 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/helpers/lexer_helper.rb +5 -0
- data/spec/support/matchers/be_token.rb +9 -0
- data/spec/support/matchers/compile.rb +41 -0
- data/spec/support/matchers/generate.rb +46 -0
- 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
|