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,139 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
describe Compiler::ICR do
|
5
|
+
|
6
|
+
let(:scanner) do
|
7
|
+
iterator = double("iterator")
|
8
|
+
scanner = double("scanner")
|
9
|
+
|
10
|
+
expect(scanner).to receive(:each).once.and_return(iterator)
|
11
|
+
allow(iterator).to receive(:next).and_return([
|
12
|
+
Scanner::Token.new(:number, "32", 0, 0),
|
13
|
+
Scanner::Token.new(:number, "10", 0, 0)])
|
14
|
+
allow(iterator).to receive(:peek).and_return(
|
15
|
+
Scanner::Token.new(:number, "32", 0, 0))
|
16
|
+
allow(iterator).to receive(:rewind)
|
17
|
+
scanner
|
18
|
+
end
|
19
|
+
|
20
|
+
subject do
|
21
|
+
described_class.new(scanner)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#peek" do
|
25
|
+
specify { expect(subject.peek).to be_a Scanner::Token }
|
26
|
+
specify { expect(subject.peek).to be_type :number }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#expect" do
|
30
|
+
|
31
|
+
context "with one argument" do
|
32
|
+
before do
|
33
|
+
expect(subject).to receive(:compile_number).once
|
34
|
+
end
|
35
|
+
|
36
|
+
it "calls the corresponding method" do
|
37
|
+
subject.expect :number
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with a mismatching argument" do
|
42
|
+
it "raises an error" do
|
43
|
+
expect {
|
44
|
+
subject.expect :string
|
45
|
+
}.to raise_error CompileError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with a hash argument" do
|
50
|
+
let :probe do
|
51
|
+
block = lambda {}
|
52
|
+
|
53
|
+
expect(block).to receive(:arity).once.and_return(0)
|
54
|
+
expect(block).to receive(:call).once.with(no_args)
|
55
|
+
block
|
56
|
+
end
|
57
|
+
|
58
|
+
let :bad_probe do
|
59
|
+
block = lambda {}
|
60
|
+
|
61
|
+
expect(block).to_not receive(:arity)
|
62
|
+
expect(block).to_not receive(:call)
|
63
|
+
end
|
64
|
+
|
65
|
+
context "and a symbol value" do
|
66
|
+
before do
|
67
|
+
expect(subject).to receive(:compile_something).once
|
68
|
+
end
|
69
|
+
|
70
|
+
it "calls the corresponding method" do
|
71
|
+
subject.expect :number => :something
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "and a block value" do
|
76
|
+
|
77
|
+
it "calls the corresponding block" do
|
78
|
+
subject.expect :number => probe
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "with no right action" do
|
83
|
+
it "raises an error" do
|
84
|
+
expect {
|
85
|
+
subject.expect :string => :foo
|
86
|
+
}.to raise_error CompileError
|
87
|
+
end
|
88
|
+
|
89
|
+
it "uses the catch all" do
|
90
|
+
subject.expect :string => bad_probe,
|
91
|
+
:_ => probe
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#compile" do
|
99
|
+
specify {
|
100
|
+
expect("hello = 3").to compile.and_produce([:exec,
|
101
|
+
[:_context, [:hello]], [:set, [:_variable, :hello], [:number, "3"]]
|
102
|
+
])
|
103
|
+
}
|
104
|
+
|
105
|
+
specify {
|
106
|
+
expect("{hello: 'world}").to compile.and_produce([:exec,
|
107
|
+
[:_context, []], [:object, [[[:identifier, "hello"], [:sstring, "world"]]]]
|
108
|
+
])
|
109
|
+
}
|
110
|
+
|
111
|
+
specify {
|
112
|
+
expect("(test)-> { 2 }").to compile.and_produce([:exec,
|
113
|
+
[:_context, []], [:function,
|
114
|
+
[:exec,
|
115
|
+
[:_context, [:test]],
|
116
|
+
[:_arguments, [[:identifier, "test"]]],
|
117
|
+
[:number, "2"]
|
118
|
+
]
|
119
|
+
]])
|
120
|
+
}
|
121
|
+
|
122
|
+
specify { expect("(2)").to compile }
|
123
|
+
specify { expect("(2)->").to_not compile }
|
124
|
+
specify { expect("()-> {}").to compile }
|
125
|
+
specify { expect("(test)-> {}").to compile }
|
126
|
+
specify { expect("(test, foo)-> {}").to compile }
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "with fixtures" do
|
130
|
+
Dir.glob("spec/fixtures/*.compile.yml") do |file|
|
131
|
+
content = YAML.load_file file
|
132
|
+
file =~ /spec\/fixtures\/(.*)\.compile\.yml/
|
133
|
+
|
134
|
+
it "compiles #{$1}" do
|
135
|
+
expect(content["data"]).to compile.and_produce content["compiled"]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
describe Liquidscript::Generator::Javascript do
|
5
|
+
describe "with fixtures" do
|
6
|
+
Dir.glob("spec/fixtures/*.generate.yml") do |file|
|
7
|
+
content = YAML.load_file file
|
8
|
+
file =~ /spec\/fixtures\/(.*)\.generate\.yml/
|
9
|
+
|
10
|
+
it "generates #{$1}" do
|
11
|
+
expect(content["data"]).to generate(content["compiled"])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
File without changes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Liquidscript
|
4
|
+
|
5
|
+
describe ICR::Context do
|
6
|
+
|
7
|
+
it "creates a variable" do
|
8
|
+
expect(subject.set(:test)).to be_a ICR::Variable
|
9
|
+
end
|
10
|
+
|
11
|
+
it "gives an invalid reference" do
|
12
|
+
expect {
|
13
|
+
subject.get(:test)
|
14
|
+
}.to raise_error Liquidscript::InvalidReferenceError
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with a parent" do
|
18
|
+
|
19
|
+
let(:parent) do
|
20
|
+
parent = double("parent")
|
21
|
+
expect(parent).to receive(:get).once.with(:foo).and_return(:test)
|
22
|
+
parent
|
23
|
+
end
|
24
|
+
|
25
|
+
subject do
|
26
|
+
context = ICR::Context.new
|
27
|
+
context.parent = parent
|
28
|
+
context
|
29
|
+
end
|
30
|
+
|
31
|
+
it "retrives the variable" do
|
32
|
+
expect(subject.get(:foo)).to be :test
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Liquidscript
|
4
|
+
|
5
|
+
describe ICR::Set do
|
6
|
+
|
7
|
+
its(:metadata) { should be_a Hash }
|
8
|
+
|
9
|
+
describe "#to_a" do
|
10
|
+
specify { expect(subject.to_a).to be_an Array }
|
11
|
+
specify { expect(subject.to_a).to have(1).item }
|
12
|
+
|
13
|
+
context "with metadata" do
|
14
|
+
subject do
|
15
|
+
set = ICR::Set.new
|
16
|
+
set.metadata.merge! :hello => "world"
|
17
|
+
set
|
18
|
+
end
|
19
|
+
|
20
|
+
specify { expect(subject.to_a).to have(2).items }
|
21
|
+
specify { expect(subject.to_a).to eq [:exec, [:_hello, "world"]] }
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with codes" do
|
26
|
+
subject do
|
27
|
+
set = ICR::Set.new
|
28
|
+
set << "test"
|
29
|
+
set
|
30
|
+
end
|
31
|
+
|
32
|
+
specify { expect(subject.to_a).to have(2).item }
|
33
|
+
specify { expect(subject.to_a).to eq [:exec, "test"] }
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with both" do
|
38
|
+
subject do
|
39
|
+
set = ICR::Set.new
|
40
|
+
set.metadata.merge! :hello => "world"
|
41
|
+
set << "test"
|
42
|
+
set
|
43
|
+
end
|
44
|
+
|
45
|
+
specify { expect(subject.to_a).to have(3).items }
|
46
|
+
specify { expect(subject.to_a).to eq [:exec, [:_hello, "world"], "test"] }
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#add" do
|
52
|
+
it "adds a code to the set" do
|
53
|
+
subject.add(:number, "32")
|
54
|
+
|
55
|
+
expect(subject.to_a).to have(2).items
|
56
|
+
expect(subject.to_a.last).to be_a ICR::Code
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Liquidscript::Scanner::Lexer, :lexer_helper do
|
4
|
+
subject { described_class.new }
|
5
|
+
describe "#emit" do
|
6
|
+
it "pushes a token" do
|
7
|
+
subject.instance_exec { @data = "hello"; @ts = 0; @te = 6 }
|
8
|
+
subject.emit(:test)
|
9
|
+
expect(subject.tokens.first).to be_token(:test, "hello")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#perform" do
|
14
|
+
it "scans a number" do
|
15
|
+
expect(scan("43")).to eq [
|
16
|
+
[:number, "43"]
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "scans a string" do
|
21
|
+
scan('"hello world" ').should eq [
|
22
|
+
[:dstring, '"hello world"']
|
23
|
+
]
|
24
|
+
|
25
|
+
scan(" 'foobar").should eq [
|
26
|
+
[:sstring, "'foobar"]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "scans an identifier" do
|
31
|
+
scan('test = 4').should eq [
|
32
|
+
[:identifier, "test"],
|
33
|
+
[:equal, "="],
|
34
|
+
[:number, "4"]
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "scans brackets" do
|
39
|
+
scan("{ test = 3 }").should eq [
|
40
|
+
[:lbrack, "{"],
|
41
|
+
[:identifier, "test"],
|
42
|
+
[:equal, "="],
|
43
|
+
[:number, "3"],
|
44
|
+
[:rbrack, "}"]
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "scans keywords" do
|
49
|
+
scan("return test = new foo").should eq [
|
50
|
+
[:keyword, "return"],
|
51
|
+
[:identifier, "test"],
|
52
|
+
[:equal, "="],
|
53
|
+
[:keyword, "new"],
|
54
|
+
[:identifier, "foo"]
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Liquidscript::Scanner do
|
4
|
+
subject { described_class.new("42") }
|
5
|
+
it { should be_a Enumerable }
|
6
|
+
|
7
|
+
describe "#each" do
|
8
|
+
specify { expect(subject.each).to be_a Enumerator }
|
9
|
+
specify { expect { |y| subject.each(&y) }.to yield_control.once }
|
10
|
+
end
|
11
|
+
|
12
|
+
context "with invalid input" do
|
13
|
+
subject { described_class.new("'test'") }
|
14
|
+
|
15
|
+
it "raises an error" do
|
16
|
+
expect {
|
17
|
+
subject.each
|
18
|
+
}.to raise_error Liquidscript::SyntaxError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require "coveralls"
|
5
|
+
Coveralls.wear!
|
6
|
+
|
7
|
+
require "liquidscript"
|
8
|
+
|
9
|
+
include Liquidscript
|
10
|
+
|
11
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
|
12
|
+
|
13
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
14
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
15
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
16
|
+
# loaded once.
|
17
|
+
#
|
18
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
21
|
+
config.run_all_when_everything_filtered = true
|
22
|
+
|
23
|
+
# Run specs in random order to surface order dependencies. If you find an
|
24
|
+
# order dependency and want to debug it, you can fix the order by providing
|
25
|
+
# the seed, which is printed after each run.
|
26
|
+
# --seed 1234
|
27
|
+
config.order = 'random'
|
28
|
+
|
29
|
+
config.include LexerHelper, :lexer_helper
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
RSpec::Matchers.define :compile do
|
2
|
+
include Liquidscript
|
3
|
+
|
4
|
+
chain :and_produce do |prod|
|
5
|
+
@prod = prod
|
6
|
+
end
|
7
|
+
|
8
|
+
match do |data|
|
9
|
+
if @prod
|
10
|
+
(@_out = compiler(data).compile) == @prod
|
11
|
+
else
|
12
|
+
@_out = compiler(data).compile?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
failure_message_for_should do |data|
|
17
|
+
"expected #{data} to compile correctly"
|
18
|
+
end
|
19
|
+
|
20
|
+
failure_message_for_should_not do |data|
|
21
|
+
"expected #{data} not to compile (compiled anyway, got: #{@_out})"
|
22
|
+
end
|
23
|
+
|
24
|
+
description do |data|
|
25
|
+
"compile #{data}"
|
26
|
+
end
|
27
|
+
|
28
|
+
diffable
|
29
|
+
|
30
|
+
def expected
|
31
|
+
@prod
|
32
|
+
end
|
33
|
+
|
34
|
+
def actual
|
35
|
+
@_out || []
|
36
|
+
end
|
37
|
+
|
38
|
+
def compiler(data)
|
39
|
+
Compiler::ICR.new(Scanner.new(data))
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
RSpec::Matchers.define :generate do |v|
|
2
|
+
include Liquidscript
|
3
|
+
|
4
|
+
match do |data|
|
5
|
+
(@_out = for_match(generator(data).generate)) ==
|
6
|
+
(@_expected = for_match(v))
|
7
|
+
end
|
8
|
+
|
9
|
+
description do |data|
|
10
|
+
"generate #{v}"
|
11
|
+
end
|
12
|
+
|
13
|
+
failure_message_for_should do |data|
|
14
|
+
sprintf "expected %{source} to generate %{dest} (got: %{actual}) - %{tree}",
|
15
|
+
:source => data.inspect,
|
16
|
+
:dest => for_match(v).inspect,
|
17
|
+
:actual => actual.inspect,
|
18
|
+
:tree => tree(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def actual
|
22
|
+
@_out
|
23
|
+
end
|
24
|
+
|
25
|
+
def expected
|
26
|
+
@_expected
|
27
|
+
end
|
28
|
+
|
29
|
+
diffable
|
30
|
+
|
31
|
+
def generator(data)
|
32
|
+
compiler = Compiler::ICR.new(Scanner.new(data))
|
33
|
+
compiler.compile
|
34
|
+
Generator::Javascript.new(compiler.top)
|
35
|
+
end
|
36
|
+
|
37
|
+
def tree(data)
|
38
|
+
compiler = Compiler::ICR.new(Scanner.new(data))
|
39
|
+
compiler.compile
|
40
|
+
compiler.top.to_sexp
|
41
|
+
end
|
42
|
+
|
43
|
+
def for_match(str)
|
44
|
+
str.to_s.gsub(/[\s\n\t]+/, '')
|
45
|
+
end
|
46
|
+
end
|