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