liquidscript 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/DOCS.md +228 -0
- data/README.md +1 -1
- data/bin/lscript +17 -0
- data/lib/liquidscript.rb +6 -1
- data/lib/liquidscript/compiler/base/helpers.rb +70 -1
- data/lib/liquidscript/compiler/icr.rb +1 -3
- data/lib/liquidscript/compiler/icr/classes.rb +1 -1
- data/lib/liquidscript/compiler/icr/expressions.rb +70 -8
- data/lib/liquidscript/compiler/icr/functions.rb +5 -8
- data/lib/liquidscript/compiler/icr/literals.rb +13 -21
- data/lib/liquidscript/generator/javascript/literals.rb +23 -2
- data/lib/liquidscript/generator/javascript/metas.rb +25 -2
- data/lib/liquidscript/icr/context.rb +1 -1
- data/lib/liquidscript/scanner/lexer.rl +13 -2
- data/lib/liquidscript/template.rb +1 -1
- data/lib/liquidscript/version.rb +1 -1
- data/lib/sprockets/liquidscript_template.rb +11 -0
- data/spec/fixtures/complex.generate.yml +14 -2
- data/spec/fixtures/operator.generate.yml +23 -0
- data/spec/fixtures/string.generate.yml +6 -1
- data/spec/fixtures/underscore.ls +29 -0
- data/spec/lib/liquidscript/scanner/lexer_spec.rb +2 -2
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2630a75e954ba63bc5da7e92040fc2fce164236b
|
4
|
+
data.tar.gz: 92fca33ae8fec68d082e16ba4431f873c8b251c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 207d7676f032a4a00237f49c99d5feacb351ff7462b6354e240ce27e32d10f0eba5f787b116b2a91eed5893fe77d2f0754c8922dfda2867050ec7695cfa380ad
|
7
|
+
data.tar.gz: 29c50870b4ed865a79ab1939b38b568353f65746ca9f3e73a6f1a01880d47f80463d1ba0e5706c9529aa56b1392d56efc19f2164db7b94b5ddecd1e4fa2469f6
|
data/DOCS.md
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# ![Liquidscript](http://i.imgur.com/xbdhTsr.png)
|
2
|
+
|
3
|
+
**A javascript-based language that compiles to javascript.** It compiles directly to javascript, requiring no runtime libraries. It means to take away the awkwardness of javascript, but keep the very essence and ideals of javascript. It incorporates some of the most well-used concepts, and allows you to write the code as you like.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'liquidscript'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install liquidscript
|
18
|
+
|
19
|
+
## Syntax
|
20
|
+
|
21
|
+
Liquidscript has a very similar syntax to Javascript.
|
22
|
+
|
23
|
+
# This is a comment
|
24
|
+
something = 2
|
25
|
+
some_string = something.toString()
|
26
|
+
|
27
|
+
if(some_string == '2) {
|
28
|
+
console.log("It lives!")
|
29
|
+
}
|
30
|
+
|
31
|
+
The thing that should stand out to you the most there is the lack of an end quote to the single quotes in the if conditional. This is on purpose - a ending single quote is optional.
|
32
|
+
|
33
|
+
Note that there are also no semicolons (they don't exist in the language), and no `var` statement. The `var` statement is made automagically by liquidscript, so you don't have to.
|
34
|
+
|
35
|
+
### Literals
|
36
|
+
|
37
|
+
#### String
|
38
|
+
|
39
|
+
Liquidscript has a very finite set of string literals - the only two options (so far) are double quotes and single quotes. There are plans to introduce heredocs. Double quotes are multiline, whereas **single quotes are limited to the same characters that identifiers are limited to**. This is so that keys for objects (in the `object[key]` syntax feels more natural (i.e., `object['key]`). Some examples:
|
40
|
+
|
41
|
+
string = "hello
|
42
|
+
|
43
|
+
world" # this'll translate to "hello\n\nworld"
|
44
|
+
|
45
|
+
single = 'test # there doesn't need to be an endquote here.
|
46
|
+
|
47
|
+
That's it!
|
48
|
+
|
49
|
+
#### Functions
|
50
|
+
|
51
|
+
Functions are defined using the arrow syntax:
|
52
|
+
|
53
|
+
some_function = -> {}
|
54
|
+
|
55
|
+
Brackets are **not** optional. The parameter list before the arrow, however, is. If you want something like coffeescript's fat arrow syntax, something like the following would have to be done:
|
56
|
+
|
57
|
+
self = this
|
58
|
+
|
59
|
+
some_function = -> {
|
60
|
+
# Change this to whatever property you wanted.
|
61
|
+
self.whatever
|
62
|
+
}
|
63
|
+
|
64
|
+
A parameter list works exactly like you'd expect:
|
65
|
+
|
66
|
+
some_function = (a, b, c)-> {
|
67
|
+
console.log(a, b, c)
|
68
|
+
}
|
69
|
+
|
70
|
+
some_function(1, 2, 3)
|
71
|
+
|
72
|
+
#### Numeric
|
73
|
+
|
74
|
+
There are numbers in liquidscript as well. It follows the JSON spec on this to the letter.
|
75
|
+
|
76
|
+
some_number = -2e+5
|
77
|
+
|
78
|
+
#### Array, Object
|
79
|
+
|
80
|
+
Arrays and objects are exactly like in javascript:
|
81
|
+
|
82
|
+
array = [1, "test", 'foo, bar]
|
83
|
+
|
84
|
+
object = {
|
85
|
+
test: "hello",
|
86
|
+
foo: "world" # a colon here is allowed
|
87
|
+
}
|
88
|
+
|
89
|
+
### Controls
|
90
|
+
|
91
|
+
Liquidscript has control statements like `if`, and `else`. Liquidscript uses `elsif` instead of `else if`.
|
92
|
+
|
93
|
+
if(some_variable == 2) {
|
94
|
+
console.log("It's 2!")
|
95
|
+
} elsif(some_variable == 3) {
|
96
|
+
console.log("It's a 3!")
|
97
|
+
} else {
|
98
|
+
console.log("I don't know what it is!")
|
99
|
+
}
|
100
|
+
|
101
|
+
Liquidscript lacks `for`, `switch`, and `while`, but they will be added as well.
|
102
|
+
|
103
|
+
### Classes and Modules
|
104
|
+
|
105
|
+
#### Modules
|
106
|
+
This is where liquidscript gets interesting, in my opinion. Modules are extreme syntaxic sugar:
|
107
|
+
|
108
|
+
module SomeModule {
|
109
|
+
VERSION: "1.7.0"
|
110
|
+
}
|
111
|
+
|
112
|
+
SomeModule.VERSION # => 1.7.0
|
113
|
+
|
114
|
+
That compiles directly to:
|
115
|
+
|
116
|
+
var SomeModule;
|
117
|
+
SomeModule = {
|
118
|
+
VERSION: "1.7.0"
|
119
|
+
};
|
120
|
+
|
121
|
+
SomeModule.VERSION; // => 1.7.0
|
122
|
+
|
123
|
+
The point of modules, however, is to bind code together into units, in an easier to read syntax. Commas are not needed between definitions, and other modules and classes can be defined within modules.
|
124
|
+
|
125
|
+
module SomeModule {
|
126
|
+
module OtherModule {
|
127
|
+
VERSION: "1.7.0"
|
128
|
+
VERSION_MAJOR: 1
|
129
|
+
VERSION_MINOR: 7
|
130
|
+
VERSION_PATCH: 0
|
131
|
+
|
132
|
+
version: -> {
|
133
|
+
return SomeModule.OtherModule.VERSION.split('.')
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
Which compiles directly to:
|
139
|
+
|
140
|
+
var SomeModule, OtherModule;
|
141
|
+
|
142
|
+
SomeModule = {
|
143
|
+
OtherModule: {
|
144
|
+
VERSION: "1.7.0",
|
145
|
+
VERSION_MAJOR: 1,
|
146
|
+
VERSION_MINOR: 7,
|
147
|
+
VERSION_PATCH: 0,
|
148
|
+
version: function() {
|
149
|
+
return SomeModule.OtherModule.VERSION.split('.');
|
150
|
+
}
|
151
|
+
}
|
152
|
+
};
|
153
|
+
|
154
|
+
#### Classes
|
155
|
+
|
156
|
+
Classes are meant to be instantized with the `new` keyword. They are defined very similarly to modules, but when values are defined, they default to being defined on the instance of the class:
|
157
|
+
|
158
|
+
class Greeter {
|
159
|
+
# This is a special function that is called whenever a
|
160
|
+
# new greeter is created.
|
161
|
+
initialize: (name)-> {
|
162
|
+
this.name = name
|
163
|
+
}
|
164
|
+
|
165
|
+
greet: -> {
|
166
|
+
console.log("Hello %s!", this.name)
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
new Greeter("Alice").greet()
|
171
|
+
|
172
|
+
This would translate directly to this:
|
173
|
+
|
174
|
+
var Greeter;
|
175
|
+
|
176
|
+
Greeter = function Greeter() {
|
177
|
+
if(this.initialize) {
|
178
|
+
this.initialize.apply(this, arguments);
|
179
|
+
}
|
180
|
+
};
|
181
|
+
|
182
|
+
Greeter.prototype.initialize = function(name) {
|
183
|
+
this.name = name;
|
184
|
+
};
|
185
|
+
|
186
|
+
Greeter.prototype.greet = function() {
|
187
|
+
console.log("Hello %s!", this.name);
|
188
|
+
};
|
189
|
+
|
190
|
+
new Greeter("Alice").greet()
|
191
|
+
|
192
|
+
This uses the functional prototype system in order to create instances of classes. Inheritance is not yet supported, but there will be progress towards it.
|
193
|
+
|
194
|
+
If you want a method defined on the class itself instead of the instance, prefix the function name with `this`:
|
195
|
+
|
196
|
+
class Greeter {
|
197
|
+
# This is a special function that is called whenever a
|
198
|
+
# new greeter is created.
|
199
|
+
initialize: (name)-> {
|
200
|
+
this.name = name
|
201
|
+
}
|
202
|
+
|
203
|
+
greet: -> {
|
204
|
+
console.log("Hello %s!", this.name)
|
205
|
+
}
|
206
|
+
|
207
|
+
self.meet: (first, second)-> {
|
208
|
+
new Greeter(first).greet()
|
209
|
+
new Greeter(second).greet()
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
Greeter.meet("Alice", "Bob")
|
214
|
+
|
215
|
+
This translates roughly to:
|
216
|
+
|
217
|
+
// ...
|
218
|
+
|
219
|
+
Greeter.meet = function(first, second) {
|
220
|
+
new Greeter(first).greet()
|
221
|
+
new Greeter(second).greet()
|
222
|
+
}
|
223
|
+
|
224
|
+
// ...
|
225
|
+
|
226
|
+
# The End!
|
227
|
+
|
228
|
+
That wraps it all up! If you're interested in more, checkout the [github repository](https://github.com/redjazz96/liquidscript) and read the documentation.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Liquidscript
|
1
|
+
# ![Liquidscript](http://i.imgur.com/xbdhTsr.png)
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/redjazz96/liquidscript.png?branch=master)](https://travis-ci.org/redjazz96/liquidscript) [![Coverage Status](https://coveralls.io/repos/redjazz96/liquidscript/badge.png?branch=master)](https://coveralls.io/r/redjazz96/liquidscript?branch=master) [![Code Climate](https://codeclimate.com/github/redjazz96/liquidscript.png)](https://codeclimate.com/github/redjazz96/liquidscript)
|
4
4
|
|
data/bin/lscript
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "liquidscript"
|
4
|
+
|
5
|
+
if ARGV.length < 1
|
6
|
+
puts "Usage: #{File.basename($0)} infile [outfile]"
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
10
|
+
infile = ARGV.shift
|
11
|
+
outfile = ARGV.shift || infile.gsub(/\.ls\Z/, ".js")
|
12
|
+
|
13
|
+
File.open(infile, "r") do |f|
|
14
|
+
out = Liquidscript::Template.new(f.read).render
|
15
|
+
|
16
|
+
File.open(outfile, "w") { |f| f.write out }
|
17
|
+
end
|
data/lib/liquidscript.rb
CHANGED
@@ -5,7 +5,12 @@ require "liquidscript/version"
|
|
5
5
|
require "liquidscript/scanner"
|
6
6
|
require "liquidscript/compiler"
|
7
7
|
require "liquidscript/generator"
|
8
|
+
require "liquidscript/template"
|
8
9
|
|
9
10
|
module Liquidscript
|
10
|
-
|
11
|
+
def compile(data)
|
12
|
+
compiler = Compiler::ICR.new(Scanner.new(data))
|
13
|
+
compiler.compile
|
14
|
+
Generate::Javascript.new(compiler.top)
|
15
|
+
end
|
11
16
|
end
|
@@ -83,6 +83,75 @@ module Liquidscript
|
|
83
83
|
types.any? { |type| peek.type == type }
|
84
84
|
end
|
85
85
|
|
86
|
+
# @overload collect_compiles(compile, *end_on)
|
87
|
+
# Calls the method `:compile_#{compile}` for every peeked
|
88
|
+
# token that isn't a part of `end_on`. Once it encounters
|
89
|
+
# that, it returns the values of all of those calls. If the
|
90
|
+
# last element of `end_on` is a Hash, it is merged with the
|
91
|
+
# default actions that this takes and passes it to `expect`.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# def compile_if
|
95
|
+
# shift :if
|
96
|
+
# shift :lparen
|
97
|
+
# conditional = compile_expression
|
98
|
+
# shift :rparen
|
99
|
+
# shift :lbrack
|
100
|
+
# body = collect_compiles(:expression, :rbrack)
|
101
|
+
# [:if, conditional, body]
|
102
|
+
# end
|
103
|
+
# @param compile [Symbol] the method to call.
|
104
|
+
# @param end_on [Array<Symbol>] an array of symbols to end
|
105
|
+
# the loop.
|
106
|
+
# @return [Array<Object>]
|
107
|
+
#
|
108
|
+
# @overload collect_compiles(*end_on, &block)
|
109
|
+
# Calls the block for every peeked token that isn't in
|
110
|
+
# `end_on`. Once it encounters a token that it, it returns
|
111
|
+
# the value of all of the block calls. If the
|
112
|
+
# last element of `end_on` is a Hash, it is merged with the
|
113
|
+
# default actions that this takes and passes it to `expect`.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# collect_compiles(:test) { shift :_ }
|
117
|
+
#
|
118
|
+
# @yieldreturn The value you want to be placed in the
|
119
|
+
# array.
|
120
|
+
# @param end_on [Array<Symbol>] an array of symbols to end
|
121
|
+
# the loop.
|
122
|
+
# @return [Array<Object>] the results of all of the block
|
123
|
+
# calls.
|
124
|
+
def collect_compiles(*end_on)
|
125
|
+
compiles = []
|
126
|
+
|
127
|
+
if block_given?
|
128
|
+
compile = Proc.new
|
129
|
+
else
|
130
|
+
compile = end_on.shift
|
131
|
+
end
|
132
|
+
|
133
|
+
block = Callable.new(self, compile)
|
134
|
+
|
135
|
+
hash = if end_on.last.is_a? Hash
|
136
|
+
end_on.pop.dup
|
137
|
+
else
|
138
|
+
{}
|
139
|
+
end
|
140
|
+
|
141
|
+
do_compile = action do
|
142
|
+
compiles << block.call
|
143
|
+
end
|
144
|
+
|
145
|
+
hash.merge! end_on => action.end_loop,
|
146
|
+
:_ => do_compile
|
147
|
+
|
148
|
+
loop do
|
149
|
+
expect hash
|
150
|
+
end
|
151
|
+
|
152
|
+
compiles
|
153
|
+
end
|
154
|
+
|
86
155
|
# The meat and potatos of the compiler. This maps actions to
|
87
156
|
# tokens. In its basic form, it is passed a hash, with the
|
88
157
|
# keys being token types, and the values the corresponding
|
@@ -176,7 +245,7 @@ module Liquidscript
|
|
176
245
|
# @return [Hash]
|
177
246
|
def normalize_arguments(args)
|
178
247
|
hash = if args.last.is_a? Hash
|
179
|
-
args.pop
|
248
|
+
args.pop.dup
|
180
249
|
else
|
181
250
|
{}
|
182
251
|
end
|
@@ -14,8 +14,6 @@ module Liquidscript
|
|
14
14
|
include Classes
|
15
15
|
include Helpers
|
16
16
|
|
17
|
-
always :keyword
|
18
|
-
|
19
17
|
# (see Base#reset!)
|
20
18
|
def reset!
|
21
19
|
@top = Liquidscript::ICR::Set.new
|
@@ -33,7 +31,7 @@ module Liquidscript
|
|
33
31
|
#
|
34
32
|
# @return [ICR::Code]
|
35
33
|
def compile_start
|
36
|
-
|
34
|
+
compile_expression
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -8,12 +8,36 @@ module Liquidscript
|
|
8
8
|
#
|
9
9
|
# @return [ICR::Code]
|
10
10
|
def compile_expression
|
11
|
-
expect :
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
expect :if, :unless, :class, :module, :_ => :vexpression
|
12
|
+
end
|
13
|
+
|
14
|
+
# Compiles an expression that returns a value.
|
15
|
+
#
|
16
|
+
# @return [ICR::Code]
|
17
|
+
def compile_vexpression
|
18
|
+
out = expect :number, :identifier,
|
19
|
+
:dstring, :lparen,
|
20
|
+
:sstring, :operator,
|
21
|
+
:keyword, :unop,
|
22
|
+
:lbrack => :object,
|
23
|
+
:lbrace => :array,
|
24
|
+
:arrow => :function
|
25
|
+
|
26
|
+
if peek? :binop
|
27
|
+
compile_binop out
|
28
|
+
elsif peek? :prop
|
29
|
+
compile_property(out)
|
30
|
+
else
|
31
|
+
out
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def compile_binop(left)
|
36
|
+
code :binop, shift(:binop), left, compile_vexpression
|
37
|
+
end
|
38
|
+
|
39
|
+
def compile_unop
|
40
|
+
code :unop, shift(:unop), compile_vexpression
|
17
41
|
end
|
18
42
|
|
19
43
|
# Handles an assignment of the form `identifier = expression`,
|
@@ -23,7 +47,7 @@ module Liquidscript
|
|
23
47
|
# @return [ICR::Code]
|
24
48
|
def compile_assignment(identifier)
|
25
49
|
shift :equal
|
26
|
-
value =
|
50
|
+
value = compile_vexpression
|
27
51
|
|
28
52
|
if identifier.type == :identifier
|
29
53
|
variable = set(identifier)
|
@@ -52,7 +76,7 @@ module Liquidscript
|
|
52
76
|
|
53
77
|
expression = action do
|
54
78
|
maybe_func = 0
|
55
|
-
components <<
|
79
|
+
components << compile_vexpression
|
56
80
|
end
|
57
81
|
|
58
82
|
loop do
|
@@ -88,6 +112,44 @@ module Liquidscript
|
|
88
112
|
end
|
89
113
|
end
|
90
114
|
|
115
|
+
[:if, :elsif].each do |key|
|
116
|
+
define_method(:"compile_#{key}") do
|
117
|
+
shift key
|
118
|
+
shift :lparen
|
119
|
+
conditional = compile_vexpression
|
120
|
+
shift :rparen
|
121
|
+
shift :lbrack
|
122
|
+
|
123
|
+
body = collect_compiles(:expression, :rbrack)
|
124
|
+
|
125
|
+
if peek?(:elsif, :else)
|
126
|
+
code key, conditional, body, expect(:elsif, :else)
|
127
|
+
else
|
128
|
+
code key, conditional, body
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def compile_unless
|
134
|
+
shift :unless
|
135
|
+
shift :lparen
|
136
|
+
conditional = compile_vexpression
|
137
|
+
shift :rparen
|
138
|
+
shift :lbrack
|
139
|
+
|
140
|
+
body = collect_compiles(:expression, :rbrack)
|
141
|
+
code :unless, conditional, body
|
142
|
+
end
|
143
|
+
|
144
|
+
def compile_else
|
145
|
+
shift :else
|
146
|
+
shift :lbrack
|
147
|
+
|
148
|
+
body = collect_compiles(:expression, :rbrack)
|
149
|
+
|
150
|
+
code :else, body
|
151
|
+
end
|
152
|
+
|
91
153
|
end
|
92
154
|
end
|
93
155
|
end
|
@@ -6,7 +6,7 @@ module Liquidscript
|
|
6
6
|
def compile_property(prop)
|
7
7
|
shift :prop
|
8
8
|
|
9
|
-
ref = if prop.type
|
9
|
+
ref = if [:identifier, :class, :module].include?(prop.type)
|
10
10
|
ref(prop)
|
11
11
|
else
|
12
12
|
prop
|
@@ -16,7 +16,8 @@ module Liquidscript
|
|
16
16
|
code :property, ref, ident
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
# Just in case there is a property named 'class' or 'module'
|
20
|
+
code = expect [:identifier, :class, :module] => property
|
20
21
|
|
21
22
|
expect :lparen => action { compile_call(code) },
|
22
23
|
:equal => action { compile_assignment(code) },
|
@@ -26,13 +27,9 @@ module Liquidscript
|
|
26
27
|
|
27
28
|
def compile_call(subject)
|
28
29
|
shift :lparen
|
29
|
-
arguments = []
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
:rparen => action.end_loop,
|
34
|
-
:_ => action { arguments << compile_expression }
|
35
|
-
end
|
31
|
+
arguments = collect_compiles :expression, :rparen,
|
32
|
+
:comma => action.shift
|
36
33
|
|
37
34
|
code :call, subject, *arguments
|
38
35
|
end
|
@@ -26,22 +26,20 @@ module Liquidscript
|
|
26
26
|
code :sstring, pop.value[1..-1]
|
27
27
|
end
|
28
28
|
|
29
|
+
def compile_operator
|
30
|
+
code :operator, shift(:operator), compile_vexpression
|
31
|
+
end
|
32
|
+
|
29
33
|
def compile_keyword
|
30
|
-
code :keyword, shift(:keyword)
|
34
|
+
code :keyword, shift(:keyword)
|
31
35
|
end
|
32
36
|
|
33
37
|
def compile_object
|
34
38
|
shift :lbrack
|
35
39
|
|
36
|
-
objects =
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
loop do
|
42
|
-
expect :rbrack => action.end_loop,
|
43
|
-
:comma => action.shift,
|
44
|
-
[:identifier, :dstring] => compile_object
|
40
|
+
objects = collect_compiles :rbrack,
|
41
|
+
:comma => action.shift do
|
42
|
+
[compile_object_key, compile_vexpression]
|
45
43
|
end
|
46
44
|
|
47
45
|
code :object, objects
|
@@ -50,14 +48,8 @@ module Liquidscript
|
|
50
48
|
def compile_array
|
51
49
|
shift :lbrace
|
52
50
|
|
53
|
-
parts =
|
54
|
-
|
55
|
-
|
56
|
-
loop do
|
57
|
-
expect :rbrace => action.end_loop,
|
58
|
-
:comma => action.shift,
|
59
|
-
:_ => compile_part
|
60
|
-
end
|
51
|
+
parts = collect_compiles(:vexpression, :rbrace,
|
52
|
+
:comma => action.shift)
|
61
53
|
|
62
54
|
code :array, parts
|
63
55
|
end
|
@@ -73,17 +65,17 @@ module Liquidscript
|
|
73
65
|
compile_function_with_parameters([])
|
74
66
|
end
|
75
67
|
|
76
|
-
def compile_function_with_parameters(
|
68
|
+
def compile_function_with_parameters(parameters)
|
77
69
|
shift :arrow
|
78
70
|
shift :lbrack
|
79
71
|
|
80
72
|
expressions = Liquidscript::ICR::Set.new
|
81
73
|
expressions.context = Liquidscript::ICR::Context.new
|
82
74
|
expressions.context.parent = top.context
|
83
|
-
expressions[:arguments] =
|
75
|
+
expressions[:arguments] = parameters
|
84
76
|
@set << expressions
|
85
77
|
|
86
|
-
|
78
|
+
parameters.each do |parameter|
|
87
79
|
set(parameter).parameter!
|
88
80
|
end
|
89
81
|
|
@@ -3,6 +3,15 @@ module Liquidscript
|
|
3
3
|
class Javascript < Base
|
4
4
|
module Literals
|
5
5
|
|
6
|
+
BINOP_SWITCH = {
|
7
|
+
"==" => "===",
|
8
|
+
"===" => "==",
|
9
|
+
"!=" => "!==",
|
10
|
+
"!==" => "!=",
|
11
|
+
"or" => "||",
|
12
|
+
"and" => "&&"
|
13
|
+
}.freeze
|
14
|
+
|
6
15
|
def generate_number(code)
|
7
16
|
|
8
17
|
"#{code.first}"
|
@@ -10,7 +19,7 @@ module Liquidscript
|
|
10
19
|
|
11
20
|
def generate_dstring(code)
|
12
21
|
|
13
|
-
"\"#{code.first.gsub(
|
22
|
+
"\"#{code.first.gsub("\n", "\\n")}\""
|
14
23
|
end
|
15
24
|
|
16
25
|
def generate_sstring(code)
|
@@ -18,10 +27,22 @@ module Liquidscript
|
|
18
27
|
"'#{code.first.gsub(/'/, "\\'")}'"
|
19
28
|
end
|
20
29
|
|
21
|
-
def
|
30
|
+
def generate_unop(code)
|
22
31
|
" #{code[1].value} #{replace(code[2])}"
|
23
32
|
end
|
24
33
|
|
34
|
+
def generate_binop(code)
|
35
|
+
op = BINOP_SWITCH.fetch(code[1].value) do
|
36
|
+
code[1].value
|
37
|
+
end
|
38
|
+
|
39
|
+
" #{replace(code[2])} #{op} #{replace(code[3])}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def generate_keyword(code)
|
43
|
+
" #{code[1].value} "
|
44
|
+
end
|
45
|
+
|
25
46
|
def generate_object(code)
|
26
47
|
|
27
48
|
object = buffer
|
@@ -4,7 +4,6 @@ module Liquidscript
|
|
4
4
|
module Metas
|
5
5
|
|
6
6
|
def generate_exec(code)
|
7
|
-
|
8
7
|
exec = buffer
|
9
8
|
exec << _exec_context(code)
|
10
9
|
code.codes.inject(exec) do |m, c|
|
@@ -23,10 +22,34 @@ module Liquidscript
|
|
23
22
|
end
|
24
23
|
|
25
24
|
def generate_get(code)
|
26
|
-
|
27
25
|
"#{code[1].name}"
|
28
26
|
end
|
29
27
|
|
28
|
+
{ :if => "if(%s)",
|
29
|
+
:elsif => "else if(%s)",
|
30
|
+
:unless => "if(!(%s))" }.each do |k, v|
|
31
|
+
|
32
|
+
define_method(:"generate_#{k}") do |code|
|
33
|
+
part = buffer
|
34
|
+
part << "\n#{v % replace(code[1])} {\n"
|
35
|
+
code[2].inject(part) { |m, p| m << replace(p) }
|
36
|
+
part << "\n}\n"
|
37
|
+
|
38
|
+
if code[3]
|
39
|
+
part << replace(code[3])
|
40
|
+
else
|
41
|
+
part
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_else(code)
|
47
|
+
part = buffer
|
48
|
+
part << "\nelse {\n"
|
49
|
+
code[1].inject(part) { |m, p| m << replace(p) }
|
50
|
+
part << "\n}\n"
|
51
|
+
end
|
52
|
+
|
30
53
|
protected
|
31
54
|
|
32
55
|
def _exec_context(code)
|
@@ -12,7 +12,7 @@ module Liquidscript
|
|
12
12
|
# The variables that are allowed to be used as a global scope,
|
13
13
|
# i.e. used in a `get` context without a previous `set`.
|
14
14
|
DEFAULT_ALLOWED_VARIABLES = [
|
15
|
-
:window, :global, :exports, :console, :this
|
15
|
+
:window, :global, :exports, :console, :this, :arguments
|
16
16
|
]
|
17
17
|
|
18
18
|
# The parent of the current context.
|
@@ -17,6 +17,12 @@
|
|
17
17
|
string_double = '"' ( any -- '"' | '\\"' )* '"';
|
18
18
|
identifier = [A-Za-z_$][A-Za-z0-9_$]*;
|
19
19
|
string_single = "'" [A-Za-z0-9_$\-]+;
|
20
|
+
keywords = 'undefined' | 'null' | 'true' | 'false';
|
21
|
+
unops = '!' | '++' | '--' | '~' | 'new' | 'return' |
|
22
|
+
'typeof';
|
23
|
+
binops = '+' | '-' | '*' | '/' | '&' | '|' | '^' | '<<' | '>>' |
|
24
|
+
'>>>' | '==' | '!=' | '===' | '!==' | '>' | '>=' | '<' | '<=' |
|
25
|
+
'&&' | '||' | 'instanceof' | 'or' | 'and';
|
20
26
|
|
21
27
|
main := |*
|
22
28
|
number => { emit :number };
|
@@ -24,8 +30,13 @@
|
|
24
30
|
string_single => { emit :sstring };
|
25
31
|
'class' => { emit :class };
|
26
32
|
'module' => { emit :module };
|
27
|
-
'
|
28
|
-
'
|
33
|
+
'if' => { emit :if };
|
34
|
+
'unless' => { emit :unless };
|
35
|
+
'elsif' => { emit :elsif };
|
36
|
+
'else' => { emit :else };
|
37
|
+
unops => { emit :unop };
|
38
|
+
binops => { emit :binop };
|
39
|
+
keywords => { emit :keyword };
|
29
40
|
identifier => { emit :identifier };
|
30
41
|
'->' => { emit :arrow };
|
31
42
|
'=' => { emit :equal };
|
data/lib/liquidscript/version.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
data: |
|
2
2
|
thing = {
|
3
3
|
test: -> {
|
4
|
-
|
4
|
+
if(undefined) {
|
5
|
+
"test"
|
6
|
+
} elsif(null) {
|
7
|
+
"um"
|
8
|
+
} else {
|
9
|
+
"hello"
|
10
|
+
}
|
5
11
|
}
|
6
12
|
}
|
7
13
|
|
@@ -12,7 +18,13 @@ compiled: |
|
|
12
18
|
|
13
19
|
thing = {
|
14
20
|
"test": function() {
|
15
|
-
|
21
|
+
if(undefined) {
|
22
|
+
"test"
|
23
|
+
} else if(null) {
|
24
|
+
"um"
|
25
|
+
} else {
|
26
|
+
"hello"
|
27
|
+
}
|
16
28
|
}
|
17
29
|
};
|
18
30
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
data: |
|
2
|
+
nodejs = -> {
|
3
|
+
out = false
|
4
|
+
|
5
|
+
if(typeof global != 'undefined) {
|
6
|
+
out = true
|
7
|
+
}
|
8
|
+
|
9
|
+
return out
|
10
|
+
}
|
11
|
+
|
12
|
+
compiled: |
|
13
|
+
var nodejs;
|
14
|
+
|
15
|
+
nodejs = function() {
|
16
|
+
var out;
|
17
|
+
out = false;
|
18
|
+
if(typeof global !== 'undefined') {
|
19
|
+
out = true;
|
20
|
+
}
|
21
|
+
|
22
|
+
return out
|
23
|
+
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
(-> {
|
2
|
+
root = this
|
3
|
+
previousUnderscore = root._
|
4
|
+
breaker = {}
|
5
|
+
ArrayProto = root.Array.prototype
|
6
|
+
ObjProto = root.Object.prototype
|
7
|
+
FuncProto = root.Function.prototype
|
8
|
+
|
9
|
+
push = ArrayProto.push
|
10
|
+
slice = ArrayProto.slice
|
11
|
+
concat = ArrayProto.concat
|
12
|
+
toString = ObjProto.toString
|
13
|
+
hasOwnProperty = ObjProto.hasOwnProperty
|
14
|
+
|
15
|
+
class _ {
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
if(typeof exports != 'undefined) {
|
20
|
+
if(typeof root.module !== 'undefined && root.module.exports) {
|
21
|
+
exports = root.module.exports = _
|
22
|
+
}
|
23
|
+
|
24
|
+
exports._ = _
|
25
|
+
} else {
|
26
|
+
root._ = _
|
27
|
+
}
|
28
|
+
_.VERSION = "1.6.0"
|
29
|
+
}).call()
|
@@ -47,10 +47,10 @@ describe Liquidscript::Scanner::Lexer, :lexer_helper do
|
|
47
47
|
|
48
48
|
it "scans keywords" do
|
49
49
|
scan("return test = new foo").should eq [
|
50
|
-
[:
|
50
|
+
[:unop, "return"],
|
51
51
|
[:identifier, "test"],
|
52
52
|
[:equal, "="],
|
53
|
-
[:
|
53
|
+
[:unop, "new"],
|
54
54
|
[:identifier, "foo"]
|
55
55
|
]
|
56
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: liquidscript
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Rodi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,18 +83,21 @@ dependencies:
|
|
83
83
|
description: A javascript-based language that compiles to javascript.
|
84
84
|
email:
|
85
85
|
- redjazz96@gmail.com
|
86
|
-
executables:
|
86
|
+
executables:
|
87
|
+
- lscript
|
87
88
|
extensions: []
|
88
89
|
extra_rdoc_files: []
|
89
90
|
files:
|
90
91
|
- ".gitignore"
|
91
92
|
- ".rspec"
|
92
93
|
- ".travis.yml"
|
94
|
+
- DOCS.md
|
93
95
|
- Gemfile
|
94
96
|
- Guardfile
|
95
97
|
- LICENSE.txt
|
96
98
|
- README.md
|
97
99
|
- Rakefile
|
100
|
+
- bin/lscript
|
98
101
|
- lib/liquidscript.rb
|
99
102
|
- lib/liquidscript/buffer.rb
|
100
103
|
- lib/liquidscript/compiler.rb
|
@@ -131,6 +134,7 @@ files:
|
|
131
134
|
- lib/liquidscript/scanner/token.rb
|
132
135
|
- lib/liquidscript/template.rb
|
133
136
|
- lib/liquidscript/version.rb
|
137
|
+
- lib/sprockets/liquidscript_template.rb
|
134
138
|
- liquidscript.gemspec
|
135
139
|
- spec/fixtures/class.compile.yml
|
136
140
|
- spec/fixtures/class.generate.yml
|
@@ -141,8 +145,10 @@ files:
|
|
141
145
|
- spec/fixtures/get.generate.yml
|
142
146
|
- spec/fixtures/literals.generate.yml
|
143
147
|
- spec/fixtures/main.compile.yml
|
148
|
+
- spec/fixtures/operator.generate.yml
|
144
149
|
- spec/fixtures/set.generate.yml
|
145
150
|
- spec/fixtures/string.generate.yml
|
151
|
+
- spec/fixtures/underscore.ls
|
146
152
|
- spec/lib/liquidscript/buffer_spec.rb
|
147
153
|
- spec/lib/liquidscript/compiler/icr_spec.rb
|
148
154
|
- spec/lib/liquidscript/generator/javascript_spec.rb
|
@@ -191,8 +197,10 @@ test_files:
|
|
191
197
|
- spec/fixtures/get.generate.yml
|
192
198
|
- spec/fixtures/literals.generate.yml
|
193
199
|
- spec/fixtures/main.compile.yml
|
200
|
+
- spec/fixtures/operator.generate.yml
|
194
201
|
- spec/fixtures/set.generate.yml
|
195
202
|
- spec/fixtures/string.generate.yml
|
203
|
+
- spec/fixtures/underscore.ls
|
196
204
|
- spec/lib/liquidscript/buffer_spec.rb
|
197
205
|
- spec/lib/liquidscript/compiler/icr_spec.rb
|
198
206
|
- spec/lib/liquidscript/generator/javascript_spec.rb
|