tequila 0.2.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.
- data/.gitignore +2 -0
- data/Changelog +18 -0
- data/MIT-LICENSE +22 -0
- data/README.markdown +213 -0
- data/Rakefile +38 -0
- data/TODO +15 -0
- data/VERSION +1 -0
- data/init.rb +3 -0
- data/lib/preprocessor.rb +71 -0
- data/lib/tequila.treetop +266 -0
- data/lib/tequila_jazz_handler.rb +30 -0
- data/lib/tree.rb +315 -0
- data/test/bench.rb +25 -0
- data/test/db/database.yml +3 -0
- data/test/db/fixtures/humans.yml +12 -0
- data/test/db/fixtures/pet_types.yml +9 -0
- data/test/db/fixtures/pets.yml +19 -0
- data/test/db/fixtures/toys.yml +12 -0
- data/test/db/preparing.rb +76 -0
- data/test/pets.jazz +3 -0
- data/test/tequila_test.rb +392 -0
- data/test/test_helper.rb +9 -0
- metadata +95 -0
data/.gitignore
ADDED
data/Changelog
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
2010-05-08 Eugene Hlyzov <hlyzov@gmail.com>
|
2
|
+
allow space between '-' and variable declaration
|
3
|
+
meaningful aliases for control symbols
|
4
|
+
add ability to omit some control symbols
|
5
|
+
allow using ' symbol to stress a variable
|
6
|
+
add 'pick all' and 'drop all' instructions
|
7
|
+
2010-01-15 Eugene Hlyzov <hlyzov@gmail.com>
|
8
|
+
[feature] Add configuration options
|
9
|
+
please check README - there is a change in default behaviour
|
10
|
+
2009-11-01 Eugene Hlyzov <hlyzov@gmail.com>
|
11
|
+
[feature] Add ability to specify static values
|
12
|
+
2009-09-07 Eugene Hlyzov <hlyzov@gmail.com>
|
13
|
+
[feature] Add ability to suppress labels
|
14
|
+
minor refactoring
|
15
|
+
2009-09-06 Eugene Hlyzov <hlyzov@gmail.com>
|
16
|
+
fix bug with nil association
|
17
|
+
fix bug with wrong label
|
18
|
+
add tests for fixed bugs
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009 Eugene Hlyzov (hlyzov[at]gmail[dot]com)
|
2
|
+
Ivan Nemytchenko (nemytchenko[at]gmail[dot]com)
|
3
|
+
Alexandr Alexandrov (elequtree[at]gmail[dot]com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
## Overview ##
|
2
|
+
|
3
|
+
Tequila basically is HAML for JSON generation.
|
4
|
+
|
5
|
+
Today while developing web applications with rich user UI, almost everything you need from rails backend is bunch of data in JSON format.
|
6
|
+
|
7
|
+
- Almost always, when you had to create unobvious set of data, your controllers became fat and ugly.
|
8
|
+
|
9
|
+
@humans.to_json(
|
10
|
+
:methods => [:login, :enhanced_name, :hello_world], :except => [:created_at, :updated_at],
|
11
|
+
:include => {:pets => { :except => [:human_id, :pet_type_id, :created_at, :updated_at],
|
12
|
+
:methods => :pet_type_name,
|
13
|
+
:include => { :toys => { :methods => [:railsize2], :only => [:color, :size]} }
|
14
|
+
}}
|
15
|
+
)
|
16
|
+
|
17
|
+
- And you had to create a lot of small helper methods in your models if you want for example fullname instead of firstname, middlename and lastname or you want pretty address, instead of separate fields for zip, country, state and street adress in your JSON.
|
18
|
+
- And you had to create child node in your JSON for has-one or belongs-to associations if you want to get even one attribute form association.
|
19
|
+
- You cannot rename keys in your json. You might want to use keyword "type", but it is reserved by rails.
|
20
|
+
- What about calling your method with some parameters while generating JSON? No native way, sorry...
|
21
|
+
|
22
|
+
Tequila is an instrument, which lets easily move your JSON-generation logic from controllers to views. Take a look at features:
|
23
|
+
|
24
|
+
## Features ##
|
25
|
+
|
26
|
+

|
27
|
+
|
28
|
+
- [Here is Jazz template](http://gist.github.com/173339/)
|
29
|
+
- [And here is it's JSON output](http://gist.github.com/173255/)
|
30
|
+
|
31
|
+
## Installation ##
|
32
|
+
|
33
|
+
./script/plugin install git://github.com/inem/tequila.git
|
34
|
+
|
35
|
+
After that drop some Tequila code into an apropriate template (should have .jazz extension) - and you are there!
|
36
|
+
|
37
|
+
## Examples ##
|
38
|
+
|
39
|
+
-@humans => people
|
40
|
+
:only
|
41
|
+
.name => login
|
42
|
+
:code => enhanced_name
|
43
|
+
name + "!!!"
|
44
|
+
:code => hello_world
|
45
|
+
"Hello world!"
|
46
|
+
+pets
|
47
|
+
:except
|
48
|
+
.human_id
|
49
|
+
.pet_type_id
|
50
|
+
+toys
|
51
|
+
:methods
|
52
|
+
.railsize("$", price.to_s)
|
53
|
+
+pet_type
|
54
|
+
:only
|
55
|
+
.class_name
|
56
|
+
|
57
|
+
-@humans => aa
|
58
|
+
:except
|
59
|
+
.name
|
60
|
+
:code => super_name
|
61
|
+
"→#{name}→"
|
62
|
+
+pets
|
63
|
+
:except
|
64
|
+
.human_id
|
65
|
+
.pet_type_id
|
66
|
+
+toys
|
67
|
+
:methods
|
68
|
+
.railsize("$", price.to_s)
|
69
|
+
+pet_type
|
70
|
+
:only
|
71
|
+
.class_name
|
72
|
+
|
73
|
+
-@humans => humanoids
|
74
|
+
:only
|
75
|
+
.name => login
|
76
|
+
:code => humanoid_names
|
77
|
+
name + "is Humanoid"
|
78
|
+
+pets => animals
|
79
|
+
:only
|
80
|
+
.human_id
|
81
|
+
.name
|
82
|
+
:code => humanoid_animals
|
83
|
+
name + "is Humanoid pet"
|
84
|
+
+toys
|
85
|
+
:only
|
86
|
+
.label
|
87
|
+
<pet_type
|
88
|
+
:only
|
89
|
+
.class_name => pet_type
|
90
|
+
|
91
|
+
## Basic syntax ##
|
92
|
+
|
93
|
+
Have you ever used to_json method in your Rails app? If so, you should be familiar with basic Tequila syntax. You still have your :only, :except and :methods keywords. Instead of writing
|
94
|
+
|
95
|
+
:include => { :association1 => {...}}
|
96
|
+
|
97
|
+
You should just remember that every variable definition must be started '-' and every association with '+'.
|
98
|
+
So example above can be written much shortly:
|
99
|
+
|
100
|
+
+association1
|
101
|
+
|
102
|
+
## Advanced ##
|
103
|
+
|
104
|
+
### Labels ###
|
105
|
+
|
106
|
+
Label can be defined via expression (=> label) which can be added everywhere where it makes sense. It means that you can add label to such elements as :code, +association, etc.. and can not add label to :except declaration and :gluening object. Some functionality of these features are covered by standart Rails to_json method and some aren't. For instance, if you bind alias 'animals' to assoication 'pets' it will automatically leads to bound 'animal' label to each Pet model below such declaration (see [example](http://gist.github.com/173255/))
|
107
|
+
|
108
|
+
### Call methods with params ###
|
109
|
+
|
110
|
+
It is pretty simple. You just can pass any amount of params in any method in :methods definition. You should not create any presenters for model methods if you want to call them with params. Want to use instance method as param? No problem! With a bit of Tequila magic you can simple do so:
|
111
|
+
|
112
|
+
...
|
113
|
+
:methods
|
114
|
+
.calculate(1,2,3, total_count)
|
115
|
+
|
116
|
+
### Code blocks ###
|
117
|
+
|
118
|
+
Code block are extremely useful if you need to add evaluable expression in the generated json but you don't want to store it as model (or presenter) method. For example you have attribute label, but for some reason you have to return "[# {label}]" just in one place of the code. The following feature allows you to keep your models clean:
|
119
|
+
|
120
|
+
...
|
121
|
+
:code => plabel
|
122
|
+
'[' + label + ']'
|
123
|
+
end
|
124
|
+
|
125
|
+
As result, in the generated data structure you will have additional field 'plabel' with desired value.
|
126
|
+
|
127
|
+
### Gluing ###
|
128
|
+
|
129
|
+
It is another nice feature which you always want to have (may be instinctively ;). Sometimes you have 'belongs_to' association where you need just one attribute. And 'gluing' allows you to.. glue attributes from child associations. Compare:
|
130
|
+
|
131
|
+
...
|
132
|
+
:tag
|
133
|
+
:only
|
134
|
+
.label
|
135
|
+
+tagger
|
136
|
+
:only
|
137
|
+
.name
|
138
|
+
end
|
139
|
+
# Out: {'tag' => {'label' => 'Happy Christmas!', 'tagger' => {'name' => 'Mr Lawrence"}}
|
140
|
+
|
141
|
+
Generated json fragment looks like too comprehensive. Let us rewrite this fragment using gluing feature:
|
142
|
+
|
143
|
+
...
|
144
|
+
:tag
|
145
|
+
:only
|
146
|
+
.label
|
147
|
+
<tagger
|
148
|
+
:only
|
149
|
+
.name => tagger_name
|
150
|
+
end
|
151
|
+
# Out: {'tag' => {'label' => 'Happy Christmas!', 'tagger_name' => 'Mr Lawrence"}}
|
152
|
+
|
153
|
+
Of course it can be handled via additional model methods, but latter is mere artifical solution. We are going to DRY, aren't we?
|
154
|
+
|
155
|
+
### Configuration ###
|
156
|
+
|
157
|
+
Since 0.1.3 there are ability to configure initial label rendering. Configuration options can be inserted either globally via Tequila::Config::Default class or locally with following syntax:
|
158
|
+
|
159
|
+
#!some_configure_option
|
160
|
+
USUAL_TEQUILA_CODE_HERE
|
161
|
+
|
162
|
+
there are two available options:
|
163
|
+
1. hide_initial_label!
|
164
|
+
2. show_initial_label!
|
165
|
+
|
166
|
+
It has a sense in a case when you are rendering collection of
|
167
|
+
objects and don't want to output its label. Compare:
|
168
|
+
|
169
|
+
# show_initial_label! used
|
170
|
+
{"links" => [{"link" => {..}}, {"link => {..}}]}
|
171
|
+
vs
|
172
|
+
# hide_initial_label! used
|
173
|
+
[{"link" => {..}}, {"link => {..}}]
|
174
|
+
|
175
|
+
As we are going to be fully compatible with Rails, hide_initial_label! is default option now.
|
176
|
+
To cancel it you can just add following lines somewhere in initializers:
|
177
|
+
|
178
|
+
class Tequila::Config::Default
|
179
|
+
show_initial_label!
|
180
|
+
end
|
181
|
+
|
182
|
+
## Issues ##
|
183
|
+
|
184
|
+
Strict order of definitions required! All blocks are optional.
|
185
|
+
|
186
|
+
1. :only or :except
|
187
|
+
2. :methods
|
188
|
+
3. :code blocks
|
189
|
+
4. +asscociations
|
190
|
+
5. <gluening
|
191
|
+
|
192
|
+
### Benchmarks ###
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
user system total real
|
197
|
+
to_json 7.280000 0.440000 7.720000 ( 7.811976)
|
198
|
+
jazz 25.920000 1.840000 27.760000 ( 28.229717)
|
199
|
+
jazz with preparse 17.210000 1.580000 18.790000 ( 19.122837)
|
200
|
+
|
201
|
+
At least for these tests it looks like to_json is ~2.4x faster..
|
202
|
+
But for some reason you use Ruby instead of C, right? Despite of the fact that Tequila is not too fast today we are happy to have such instrument and are going to develop it further. And we have good plans about it...
|
203
|
+
|
204
|
+
### Plans ###
|
205
|
+
|
206
|
+
- Grammatic review
|
207
|
+
- Implement HashMapper fucntionality
|
208
|
+
- Arbitrary order of defenitions
|
209
|
+
- More tests for edge cases
|
210
|
+
- More syntax sugar
|
211
|
+
- Speedup
|
212
|
+
|
213
|
+
And.. we are always open for your feedback! :)
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
require 'test/bench'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test the tequila plugin.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
desc 'Benchmark'
|
20
|
+
task :bench do
|
21
|
+
TequilaBenchmark.run
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'jeweler'
|
26
|
+
Jeweler::Tasks.new do |gemspec|
|
27
|
+
gemspec.version = '0.2.1'
|
28
|
+
gemspec.name = "tequila"
|
29
|
+
gemspec.summary = "Language for advanced JSON generation"
|
30
|
+
gemspec.description = "Language for advanced JSON generation"
|
31
|
+
gemspec.email = "eugene.hlyzov@gmail.com"
|
32
|
+
gemspec.homepage = "http://github.com/inem/tequila"
|
33
|
+
gemspec.authors = ["Eugene Hlyzov", "Ivan Nemytchenko"]
|
34
|
+
end
|
35
|
+
Jeweler::GemcutterTasks.new
|
36
|
+
rescue LoadError
|
37
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
38
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
BUGS:
|
2
|
+
TODO:
|
3
|
+
- option to generate keys without quotes
|
4
|
+
- gluing for mas_many associations
|
5
|
+
- comments //
|
6
|
+
- :plain (some symbol for this?)
|
7
|
+
- :only => .aaa
|
8
|
+
.aaa .bbb
|
9
|
+
.bbb
|
10
|
+
- :except => !aaa
|
11
|
+
.aaa !bbb
|
12
|
+
.bbb
|
13
|
+
- :plain by default
|
14
|
+
- define :all by symbol
|
15
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/init.rb
ADDED
data/lib/preprocessor.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module TequilaPreprocessor
|
2
|
+
EOB = "end\n".freeze
|
3
|
+
EOC = "end\n".freeze
|
4
|
+
JAZZ = ".jazz".freeze
|
5
|
+
|
6
|
+
def TequilaPreprocessor.run(source, offset = -1)
|
7
|
+
source = replace(source)
|
8
|
+
prev = offset
|
9
|
+
text = ""
|
10
|
+
code = nil
|
11
|
+
lines = source.split("\n")
|
12
|
+
for line in lines do
|
13
|
+
unless (line.strip =~ /\/\//) == 0
|
14
|
+
spaces, rest = spaces(line)
|
15
|
+
raise "#{spaces} spaces in line <#{line}>" if (spaces & 1 != 0)
|
16
|
+
tabs = spaces >> 1
|
17
|
+
if code
|
18
|
+
if tabs <= code
|
19
|
+
code = nil
|
20
|
+
text += EOC
|
21
|
+
else
|
22
|
+
text += "#{line}\n"
|
23
|
+
next
|
24
|
+
end
|
25
|
+
end
|
26
|
+
case rest
|
27
|
+
when /^:code/
|
28
|
+
code = tabs
|
29
|
+
when /^(\+|\-|\<|source|join|merge)/
|
30
|
+
diff = prev - tabs
|
31
|
+
diff += 1 if diff >= 0
|
32
|
+
diff.times { text += EOB }
|
33
|
+
prev = tabs
|
34
|
+
end
|
35
|
+
text += "#{line}\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
text += EOC if code
|
39
|
+
(prev - offset).times { text += EOB }
|
40
|
+
text
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def TequilaPreprocessor.replace(source)
|
45
|
+
text = ''
|
46
|
+
while (i = (/(^ *)(&|include(\ )+)(.*$)/ =~ source)) do
|
47
|
+
text += source[0...i]
|
48
|
+
source = $'
|
49
|
+
spaces = $1.size
|
50
|
+
filename = $4 + ".jazz"
|
51
|
+
text += include_file(filename, spaces)
|
52
|
+
end
|
53
|
+
text += source
|
54
|
+
end
|
55
|
+
|
56
|
+
def TequilaPreprocessor.include_file(filename, offset)
|
57
|
+
lines = ''
|
58
|
+
prefix = ' ' * offset
|
59
|
+
f = open(filename, 'r')
|
60
|
+
while (s = f.gets) do
|
61
|
+
lines << prefix + s
|
62
|
+
end
|
63
|
+
f.close
|
64
|
+
lines
|
65
|
+
end
|
66
|
+
|
67
|
+
def TequilaPreprocessor.spaces(line)
|
68
|
+
(/^ +/ =~ line) ? [$&.size, $'] : [0, line]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/lib/tequila.treetop
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
grammar Tequila
|
2
|
+
|
3
|
+
rule program
|
4
|
+
allow_space configuration:configuration_line* expression allow_space {
|
5
|
+
def eval(binding)
|
6
|
+
tree = Tequila::Tree.new
|
7
|
+
configuration.elements.each {|cl| cl.eval(tree)}
|
8
|
+
expression.eval(tree, tree.root, binding)
|
9
|
+
end
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
rule configuration_line
|
14
|
+
allow_space '#!' key:term allow_space value:term? {
|
15
|
+
def eval(tree)
|
16
|
+
if value.text_value.blank?
|
17
|
+
tree.config.send(key.text_value.intern)
|
18
|
+
else
|
19
|
+
tree.config.send(key.text_value.intern, value.text_value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
rule expression
|
26
|
+
source_keyword var_type:('@'/'@@'/'$')? object {
|
27
|
+
def eval(tree, parent, binding)
|
28
|
+
object.eval(tree, parent, binding, :variable, {:var_type => var_type.text_value})
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
rule source_keyword
|
34
|
+
'-' allow_space
|
35
|
+
/ 'source' must_space
|
36
|
+
end
|
37
|
+
|
38
|
+
rule object
|
39
|
+
data_mark? object_name:term suppress_label:'~'? label:label_decl? atd:attributes_decl? mtd:methods_decl? std:statics_decl? cbd:code_decl* asd:association_decl* gld:gluing_decl* eob {
|
40
|
+
|
41
|
+
def name
|
42
|
+
object_name.text_value
|
43
|
+
end
|
44
|
+
|
45
|
+
def eval(tree, parent, binding, type, params ={})
|
46
|
+
|
47
|
+
node = Tequila::Node.new((:variable == type) ? params[:var_type] + name : name, type).eval(binding)
|
48
|
+
|
49
|
+
unless suppress_label.empty?
|
50
|
+
node.suppress_label = true
|
51
|
+
end
|
52
|
+
|
53
|
+
if label.elements
|
54
|
+
node.label = label.term.text_value
|
55
|
+
end
|
56
|
+
|
57
|
+
if atd.elements
|
58
|
+
atd.update_node(node)
|
59
|
+
end
|
60
|
+
|
61
|
+
if std.elements
|
62
|
+
std.update_node(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
if mtd.elements
|
66
|
+
mtd.update_node(node)
|
67
|
+
end
|
68
|
+
|
69
|
+
if cbd.elements
|
70
|
+
cbd.elements.each {|cb| cb.update_node(node)}
|
71
|
+
end
|
72
|
+
|
73
|
+
tree.add_child_to(parent, node)
|
74
|
+
|
75
|
+
asd.elements.each {|e| e.eval(tree, node, binding) }
|
76
|
+
gld.elements.each {|e| e.eval(tree, node, binding) }
|
77
|
+
|
78
|
+
tree
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
rule association_decl
|
84
|
+
allow_space ('+' allow_space /'join' must_space) object {
|
85
|
+
def eval(tree, node, binding)
|
86
|
+
object.eval(tree, node, binding, :association)
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
rule gluing_decl
|
92
|
+
allow_space ('<' allow_space /'merge' must_space) object {
|
93
|
+
def eval(tree, node, binding)
|
94
|
+
object.eval(tree, node, binding, :bounded)
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
rule attributes_decl
|
100
|
+
allow_space ':'? (only_attributes_decl / except_attributes_decl) {
|
101
|
+
def update_node(node)
|
102
|
+
elements[2].update_node(node)
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
rule only_attributes_decl
|
108
|
+
(allow_space atd:pick_all_decl+
|
109
|
+
/ ('only'/'pick') atd:only_attribute_decl+) {
|
110
|
+
def update_node(node)
|
111
|
+
if atd.elements
|
112
|
+
atd.elements.each {|x| x.update_node(node, :only)}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
rule except_attributes_decl
|
119
|
+
(allow_space atd:drop_all_decl+
|
120
|
+
/ ('except'/'drop') atd:except_attribute_decl+) {
|
121
|
+
def update_node(node)
|
122
|
+
if atd.elements
|
123
|
+
atd.elements.each {|x| x.update_node(node, :except)}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
rule pick_all_decl
|
130
|
+
'pick' must_space 'all' {
|
131
|
+
def update_node(node, _)
|
132
|
+
node.all_attributes!
|
133
|
+
end
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
rule drop_all_decl
|
138
|
+
'drop' must_space 'all' {
|
139
|
+
def update_node(node, _)
|
140
|
+
node.no_attributes!
|
141
|
+
end
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
rule only_attribute_decl
|
147
|
+
allow_space '.' term label:label_decl? {
|
148
|
+
def update_node(node, key)
|
149
|
+
m = Tequila::Node::Attribute.new(term.text_value)
|
150
|
+
if label.elements
|
151
|
+
m.label = label.term.text_value
|
152
|
+
end
|
153
|
+
node.add_attribute(key, m)
|
154
|
+
end
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
rule except_attribute_decl
|
159
|
+
allow_space '.' term {
|
160
|
+
def update_node(node, key)
|
161
|
+
m = Tequila::Node::Attribute.new(term.text_value)
|
162
|
+
node.add_attribute(key, m)
|
163
|
+
end
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
rule attribute_decl
|
168
|
+
allow_space '.' term label:label_decl? {
|
169
|
+
def update_node(node, key)
|
170
|
+
m = Tequila::Node::Attribute.new(term.text_value)
|
171
|
+
if label.elements
|
172
|
+
m.label = label.term.text_value
|
173
|
+
end
|
174
|
+
node.add_attribute(key, m)
|
175
|
+
end
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
rule methods_decl
|
180
|
+
allow_space ':'? 'methods' mtd:(method_decl)+ {
|
181
|
+
def update_node(node)
|
182
|
+
mtd.elements.each {|x| x.update_node(node)}
|
183
|
+
end
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
rule method_decl
|
188
|
+
allow_space '.' term params:('(' allow_space first_param:(!(','/')') .)+ allow_space rest_params:(',' allow_space param:(!(','/')') .)+ allow_space)* ')')? label:label_decl? {
|
189
|
+
def update_node(node)
|
190
|
+
m = Tequila::Node::Method.new(term.text_value)
|
191
|
+
|
192
|
+
if params.elements
|
193
|
+
m.params = parameters
|
194
|
+
end
|
195
|
+
|
196
|
+
if label.elements
|
197
|
+
m.label = label.term.text_value
|
198
|
+
end
|
199
|
+
node.methods += [m]
|
200
|
+
end
|
201
|
+
|
202
|
+
def parameters
|
203
|
+
[params.first_param.text_value] + params.rest_params.elements.map{|p| p.param.text_value}
|
204
|
+
end
|
205
|
+
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
rule statics_decl
|
210
|
+
allow_space ':'? 'static' std:(static_decl)+ {
|
211
|
+
def update_node(node)
|
212
|
+
std.elements.each {|x| x.update_node(node)}
|
213
|
+
end
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
rule static_decl
|
218
|
+
allow_space label:term label_decl {
|
219
|
+
def update_node(node)
|
220
|
+
node.statics += [Tequila::Node::Static.new(label.text_value, label_decl.term.text_value)]
|
221
|
+
end
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
rule code_decl
|
226
|
+
allow_space ':'? 'code' label_decl code:(!eob . )* code_end {
|
227
|
+
def update_node(node)
|
228
|
+
node.code_blocks += [Tequila::Node::CodeBlock.new(label_decl.term.text_value, code.text_value)]
|
229
|
+
end
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
rule label_decl
|
234
|
+
allow_space ('=>'/'label') allow_space data_mark? term
|
235
|
+
end
|
236
|
+
|
237
|
+
rule code_end
|
238
|
+
eob
|
239
|
+
end
|
240
|
+
|
241
|
+
rule eob #end of block
|
242
|
+
allow_space 'end'
|
243
|
+
end
|
244
|
+
|
245
|
+
rule term
|
246
|
+
[a-zA-Z0-9_\?\!]+
|
247
|
+
end
|
248
|
+
|
249
|
+
rule must_space
|
250
|
+
white_space+
|
251
|
+
end
|
252
|
+
|
253
|
+
rule allow_space
|
254
|
+
white_space*
|
255
|
+
end
|
256
|
+
|
257
|
+
rule white_space
|
258
|
+
[ \n\r]
|
259
|
+
end
|
260
|
+
|
261
|
+
rule data_mark
|
262
|
+
"'"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|