luobo 0.0.2 → 0.0.5
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/README.rdoc +31 -4
- data/{bin → backup/bin}/tuzi +0 -0
- data/{example → backup/example}/parser.lub +2 -2
- data/backup/lib/luobo.rb +5 -0
- data/{lib → backup/lib}/luobo/base.rb +5 -46
- data/{lib → backup/lib}/luobo/driver.rb +0 -0
- data/backup/lib/luobo/token.rb +32 -0
- data/backup/lib/luobo/version.rb +4 -0
- data/{spec → backup/spec}/driver_spec.rb +0 -0
- data/{spec → backup/spec}/parser_spec.rb +0 -0
- data/backup/spec/token_spec.rb +32 -0
- data/examples/loop_example.rb +8 -0
- data/examples/lua_loop.lua +11 -0
- data/lib/luobo.rb +219 -3
- data/lib/luobo/token.rb +36 -22
- data/lib/luobo/version.rb +1 -1
- data/luobo.gemspec +2 -3
- data/spec/block_spec.rb +79 -0
- data/spec/command_spec.rb +14 -0
- data/spec/loop_spec.rb +35 -0
- data/spec/lua_loop_spec.rb +35 -0
- data/spec/token_spec.rb +107 -16
- data/temp.rb +142 -0
- metadata +26 -14
data/README.rdoc
CHANGED
@@ -1,9 +1,36 @@
|
|
1
1
|
= Luobo
|
2
2
|
|
3
|
-
Luobo is a
|
3
|
+
Luobo is a code-generator that can expands macros defined in a source file.
|
4
4
|
|
5
|
-
|
5
|
+
Basically you use luobo by extends a Converter:
|
6
6
|
|
7
|
-
|
7
|
+
require "luobo"
|
8
|
+
class LuaSpecConverter < Luobo::Converter
|
9
|
+
def regex_comment_header; '^\s*--' end
|
8
10
|
|
9
|
-
|
11
|
+
def do_hello token
|
12
|
+
"-- Hello, " + token.line_code + "!"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
You write a lua file (`test.lua`) like:
|
17
|
+
|
18
|
+
require "some_code"
|
19
|
+
|
20
|
+
-- HELLO: world
|
21
|
+
|
22
|
+
You run:
|
23
|
+
|
24
|
+
LuaSpecConverter.new('test.lua', STDOUT).convert
|
25
|
+
|
26
|
+
Then you will get those in stdout:
|
27
|
+
|
28
|
+
require "some_code"
|
29
|
+
|
30
|
+
-- Hello, world!
|
31
|
+
|
32
|
+
TODO:
|
33
|
+
|
34
|
+
- block inside comments
|
35
|
+
- more robust tests
|
36
|
+
- remove bin file
|
data/{bin → backup/bin}/tuzi
RENAMED
File without changes
|
data/backup/lib/luobo.rb
ADDED
@@ -55,48 +55,6 @@ module Luobo
|
|
55
55
|
@driver.dump(@output, contents)
|
56
56
|
end
|
57
57
|
|
58
|
-
# travel up through the token stack, close all token with
|
59
|
-
# indent level larger or equal to the parameter
|
60
|
-
def close_stack indent_level
|
61
|
-
source = ''
|
62
|
-
while @token_stack.size > 0 and @token_stack[-1].indent_level >= indent_level
|
63
|
-
if @token_stack.size > 1
|
64
|
-
@token_stack[-2].add_block_code @driver.convert(@token_stack[-1])
|
65
|
-
else # this is the last token in the stack
|
66
|
-
self.dump(@driver.convert(@token_stack[-1]))
|
67
|
-
end
|
68
|
-
@token_stack.pop
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# add a new line to the existing token if it requires block codes
|
73
|
-
# or write the last token out before a new token
|
74
|
-
# this function invokes after a loop expansion.
|
75
|
-
def process_line line, ln, loop_n = 0
|
76
|
-
indent_level = 0
|
77
|
-
if /^(?<head_space_>\s+)/ =~ line
|
78
|
-
indent_level = head_space_.size
|
79
|
-
end
|
80
|
-
|
81
|
-
# try to close stack if applicable
|
82
|
-
self.close_stack indent_level
|
83
|
-
|
84
|
-
# starts a named_processor or starts a raw_processor
|
85
|
-
processor_name = '_raw'
|
86
|
-
line_code = line.gsub(/^\s*/,"")
|
87
|
-
block_code = nil
|
88
|
-
if matches = /#{regex_proc_line}/.match(line)
|
89
|
-
processor_name = matches["proc_name_"]
|
90
|
-
line_code = matches["line_code_"]
|
91
|
-
block_code = '' if line_code.gsub!(/#{regex_block_start}/, '')
|
92
|
-
end
|
93
|
-
|
94
|
-
@token_stack << Token.new(ln, line, indent_level, processor_name, line_code, block_code)
|
95
|
-
|
96
|
-
# unless it opens for block code close it soon,
|
97
|
-
# (dedicate the dump function to close_stack())
|
98
|
-
self.close_stack indent_level unless block_code
|
99
|
-
end
|
100
58
|
|
101
59
|
# add a new line to the example, separate examples to array
|
102
60
|
def add_example_line line
|
@@ -112,8 +70,12 @@ module Luobo
|
|
112
70
|
|
113
71
|
# regex configurations
|
114
72
|
# match characters before a processor keyword
|
73
|
+
def regex_proc_head_without_space
|
74
|
+
"(?<proc_head_>\s*(\#{1,}|\-{2,}|\/{2,}))?"
|
75
|
+
end
|
76
|
+
|
115
77
|
def regex_proc_head
|
116
|
-
|
78
|
+
regex_proc_head_without_space + '\s*'
|
117
79
|
end
|
118
80
|
|
119
81
|
def regex_proc_name
|
@@ -128,9 +90,6 @@ module Luobo
|
|
128
90
|
"^" + regex_proc_head + regex_proc_name + regex_proc_end + "(?<line_code_>.+)"
|
129
91
|
end
|
130
92
|
|
131
|
-
def regex_block_start
|
132
|
-
"\s*(?<block_start_>\-\>)?\s*$"
|
133
|
-
end
|
134
93
|
|
135
94
|
def regex_loop_line
|
136
95
|
"^" + regex_proc_head + "\%\s*\=\=\=\=+\s*"
|
File without changes
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Luobo
|
2
|
+
## this class holds a block of carrot source code tokenized by the parser.
|
3
|
+
class Token
|
4
|
+
attr_accessor :ln, :line, :indent_level, :processor_name, :line_code, :block_code
|
5
|
+
|
6
|
+
def initialize ln, line, indent_level, processor_name, line_code, block_code, proc_head = ''
|
7
|
+
@ln, @line, @indent_level, @processor_name, @line_code, @block_code, @proc_head = ln, line, indent_level, processor_name, line_code, block_code, proc_head
|
8
|
+
end
|
9
|
+
|
10
|
+
# add a line to current block args, separate each line with "\n"
|
11
|
+
def add_block_code line
|
12
|
+
line.chomp!
|
13
|
+
unless processor_name == '_raw'
|
14
|
+
line.gsub!(/^#{@proc_head}/, '')
|
15
|
+
end
|
16
|
+
|
17
|
+
if self.block_code
|
18
|
+
self.block_code += "\n" + line
|
19
|
+
else
|
20
|
+
self.block_code = line
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def block_args
|
25
|
+
YAML.load(block_code)
|
26
|
+
end
|
27
|
+
|
28
|
+
def line_args
|
29
|
+
YAML.load(line_code)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Luobo::Token do
|
4
|
+
subject (:token) do
|
5
|
+
Luobo::Token.new(1, "line", 4, "Echo", "same: hash", "['block', 'in', 'array']")
|
6
|
+
end
|
7
|
+
|
8
|
+
its :ln do
|
9
|
+
should eq(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
its :line do
|
13
|
+
should eq("line")
|
14
|
+
end
|
15
|
+
|
16
|
+
its :indent_level do
|
17
|
+
should eq(4)
|
18
|
+
end
|
19
|
+
|
20
|
+
its :processor_name do
|
21
|
+
should eq("Echo")
|
22
|
+
end
|
23
|
+
|
24
|
+
its :line_args do
|
25
|
+
token.line_args["same"].should eq("hash")
|
26
|
+
end
|
27
|
+
|
28
|
+
its :block_args do
|
29
|
+
token.block_args[2].should eq("array")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/luobo.rb
CHANGED
@@ -1,5 +1,221 @@
|
|
1
1
|
require "yaml"
|
2
|
-
|
2
|
+
require "erubis"
|
3
3
|
require "luobo/token"
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
# converter class
|
6
|
+
class Luobo
|
7
|
+
|
8
|
+
def initialize file, output
|
9
|
+
@source_file = file
|
10
|
+
|
11
|
+
# handle output file or output IO
|
12
|
+
if output.is_a?(IO)
|
13
|
+
@output = output
|
14
|
+
elsif output.is_a?(String)
|
15
|
+
@output_file = output
|
16
|
+
@output = File.open(output, "w")
|
17
|
+
end
|
18
|
+
|
19
|
+
# initialize a array to hold tokens waiting for process:
|
20
|
+
@token_stack = Array.new
|
21
|
+
|
22
|
+
# initialize loop template and examples
|
23
|
+
self.reset_loop
|
24
|
+
end
|
25
|
+
|
26
|
+
# initialize the holders for a example based loop,
|
27
|
+
# or reset after a loop expansion
|
28
|
+
def reset_loop
|
29
|
+
@loop_start_ln = 0
|
30
|
+
@loop_template = nil
|
31
|
+
@loop_examples = Array.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# extend a loop be examples
|
35
|
+
def expand_loop
|
36
|
+
raise "no examples found for loop start on line #{@loop_start_ln}" unless @loop_examples.size > 0
|
37
|
+
loop_n = 0
|
38
|
+
@loop_examples.each do |exa|
|
39
|
+
loop_n += 1
|
40
|
+
# treat each loop example as a yaml var definition for the template
|
41
|
+
rslt = Erubis::Eruby.new(@loop_template).result(YAML.load(exa))
|
42
|
+
li = 0
|
43
|
+
rslt.split("\n").each do |line|
|
44
|
+
li += 1
|
45
|
+
self.process_line @loop_start_ln + li, line
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# clear up holders
|
50
|
+
self.reset_loop
|
51
|
+
end
|
52
|
+
|
53
|
+
# add a new line to the example, separate examples to array
|
54
|
+
def add_example_line line
|
55
|
+
if /#{regex_new_example}/.match(line)
|
56
|
+
# start a new loop example with a placeholder nil
|
57
|
+
@loop_examples << nil
|
58
|
+
else
|
59
|
+
raise "you need use '#{regex_new_example}' to start a loop example" unless @loop_examples.size > 0
|
60
|
+
line.gsub!(/#{regex_example_head}/, '')
|
61
|
+
@loop_examples[-1] = @loop_examples[-1] ? @loop_examples[-1] + "\n" + line : line
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# handle convert for each token
|
66
|
+
def convert token
|
67
|
+
pname = "do_" + token.processor_name.downcase
|
68
|
+
if self.respond_to?(pname)
|
69
|
+
self.send(pname.to_sym, token)
|
70
|
+
else
|
71
|
+
self.do__missing(token)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# processor converters and dump callbacks
|
76
|
+
def do_setup; "" end # call before all tokens
|
77
|
+
def do_cleanup; "" end # call after all tokens
|
78
|
+
|
79
|
+
def do__raw token
|
80
|
+
if token.line and token.line.size > 0
|
81
|
+
token.line#.gsub(/^\s*/, "") + "\n"
|
82
|
+
else
|
83
|
+
""
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def do__missing token
|
88
|
+
src = token.line
|
89
|
+
src += token.block_code + "\n" if token.block_code
|
90
|
+
src
|
91
|
+
end
|
92
|
+
|
93
|
+
def dump contents
|
94
|
+
@output.print contents
|
95
|
+
end
|
96
|
+
|
97
|
+
# regex settings
|
98
|
+
def regex_line_comment; "" end
|
99
|
+
def regex_proc_head; "(#{regex_line_comment})?(?<leading_spaces_>\s*)" end
|
100
|
+
def regex_proc_name; "(?<processor_name_>[A-Z][_A-Z0-9]*)" end
|
101
|
+
def regex_proc_line; "^" + regex_proc_head + regex_proc_name + regex_proc_end + "(?<line_code_>.+)" end
|
102
|
+
def regex_proc_end; "\s*\:?\s+" end
|
103
|
+
def regex_block_start; "\-\>" end
|
104
|
+
|
105
|
+
# example loop related:
|
106
|
+
def regex_loop_line; "^" + regex_proc_head + "\%\s*\=\=\=\=+\s*" end
|
107
|
+
def regex_new_example; "^" + regex_proc_head + "\%\s*\-\-\-\-+\s*" end
|
108
|
+
def regex_example_head; "^" + regex_proc_head + "\%\s*" end
|
109
|
+
def regex_example_line; "^" + regex_proc_head + "\%\s*(?<example_>.+)\s*" end
|
110
|
+
|
111
|
+
# create a token from line
|
112
|
+
def tokenize ln, line
|
113
|
+
pure_line = line.gsub(/^#{regex_line_comment}/, '') # trim line comment marker
|
114
|
+
|
115
|
+
indent_level = 0
|
116
|
+
processor_name = '_raw'
|
117
|
+
line_code = ''
|
118
|
+
block_open = false
|
119
|
+
if matches = /#{regex_proc_line}/.match(pure_line)
|
120
|
+
processor_name = matches["processor_name_"]
|
121
|
+
indent_level = matches["leading_spaces_"].size
|
122
|
+
line_code = matches["line_code_"]
|
123
|
+
block_open = true if /#{regex_block_start}\s*$/ =~ line_code
|
124
|
+
line_code.gsub!(/\s*(#{regex_block_start})?\s*$/, '')
|
125
|
+
elsif matches = /^#{regex_proc_head}[^\s]/.match(pure_line)
|
126
|
+
indent_level = matches["leading_spaces_"].size
|
127
|
+
end
|
128
|
+
|
129
|
+
Token.new ln, line, indent_level, processor_name, line_code, block_open
|
130
|
+
end
|
131
|
+
|
132
|
+
# travel up through the token stack, close all token with
|
133
|
+
# indent level larger or equal to the parameter
|
134
|
+
def close_stack indent_level
|
135
|
+
while @token_stack.size > 0 and @token_stack[-1].indent_level >= indent_level
|
136
|
+
if @token_stack.size > 1 # if this is not the last token, add to parents
|
137
|
+
@token_stack[-2].add_block_code self.convert(@token_stack[-1])
|
138
|
+
else # this is the last token in the stack
|
139
|
+
dump self.convert(@token_stack[-1])
|
140
|
+
end
|
141
|
+
@token_stack.pop
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# add a new line to the existing token if it requires block codes
|
146
|
+
# or write the last token out before a new token
|
147
|
+
# this function invokes after a loop expansion or if the line
|
148
|
+
# is not in any examples and loop templates.
|
149
|
+
def process_line ln, line
|
150
|
+
# create a token, with processor name
|
151
|
+
token = self.tokenize(ln, line)
|
152
|
+
# based on the the token indent level, close token stack if any
|
153
|
+
self.close_stack token.indent_level
|
154
|
+
|
155
|
+
# add token and then close it unless it opens a block
|
156
|
+
@token_stack << token
|
157
|
+
self.close_stack token.indent_level unless token.block_open?
|
158
|
+
|
159
|
+
self # for methods chain
|
160
|
+
end
|
161
|
+
|
162
|
+
# process
|
163
|
+
def process!
|
164
|
+
fh = File.open(@source_file, 'r:utf-8')
|
165
|
+
in_loop = false
|
166
|
+
in_example = false
|
167
|
+
fh.each do |line|
|
168
|
+
line.chomp!
|
169
|
+
line.gsub!("\t", " ") # convert tab to 2 spaces
|
170
|
+
|
171
|
+
# interrupt for loops
|
172
|
+
if /#{regex_loop_line}/.match(line)
|
173
|
+
in_loop = true
|
174
|
+
@loop_start_ln = $.
|
175
|
+
next # break further processing if detecting a loop
|
176
|
+
end
|
177
|
+
|
178
|
+
# expand and end current loop
|
179
|
+
if in_example
|
180
|
+
unless /#{regex_example_head}/.match(line) # if the line not marked as an example
|
181
|
+
self.expand_loop
|
182
|
+
in_example = false
|
183
|
+
in_loop = false
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# end a loop template and start a loop example
|
188
|
+
if in_loop
|
189
|
+
in_example = true if /#{regex_example_head}/.match(line)
|
190
|
+
end
|
191
|
+
|
192
|
+
# dispatch the current line to different holders
|
193
|
+
if in_example
|
194
|
+
self.add_example_line line
|
195
|
+
elsif in_loop
|
196
|
+
@loop_template = @loop_template ? @loop_template + "\n" + line : line
|
197
|
+
else
|
198
|
+
self.process_line $., line
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
fh.close
|
203
|
+
|
204
|
+
# do not forget expand last loop if it reaches over EOF.
|
205
|
+
self.expand_loop if @loop_start_ln > 0
|
206
|
+
|
207
|
+
# close all remain stacks (if any)
|
208
|
+
self.close_stack 0
|
209
|
+
|
210
|
+
self.dump(self.do_cleanup)
|
211
|
+
@output.close if @output_file
|
212
|
+
|
213
|
+
self # return self for method chains
|
214
|
+
end
|
215
|
+
|
216
|
+
# command handler
|
217
|
+
def self.convert! file, output
|
218
|
+
self.new(file, output).process!
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
data/lib/luobo/token.rb
CHANGED
@@ -1,28 +1,42 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
## this class holds a block of carrot source code tokenized by the parser.
|
2
|
+
class Token
|
3
|
+
attr_accessor :ln, :line, :indent_level, # raw line and line number
|
4
|
+
:processor_name, :line_code, :blocks, :indent_level
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# add a line to current block args, separate each line with "\n"
|
11
|
-
def add_block_code line
|
12
|
-
line.chomp!
|
13
|
-
if self.block_code
|
14
|
-
self.block_code += "\n" + line
|
15
|
-
else
|
16
|
-
self.block_code = line
|
17
|
-
end
|
18
|
-
end
|
6
|
+
def initialize ln, line, indent_level, processor_name, line_code = '', block_open = false
|
7
|
+
@ln, @line, @indent_level, @processor_name, @line_code = ln, line, indent_level, processor_name, line_code
|
8
|
+
@blocks = Array.new if block_open
|
9
|
+
end
|
19
10
|
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
def indent
|
12
|
+
" " * indent_level
|
13
|
+
end
|
23
14
|
|
24
|
-
|
25
|
-
|
15
|
+
# add a line to current block args, separate each line with "\n"
|
16
|
+
def add_block_code line
|
17
|
+
raise "block not opened in line #{:ln}" unless block_open?
|
18
|
+
line.chomp.split("\n").each do |ln|
|
19
|
+
@blocks << ln
|
26
20
|
end
|
27
21
|
end
|
22
|
+
|
23
|
+
def has_block?
|
24
|
+
(@blocks and @blocks.size > 0) ? true : false
|
25
|
+
end
|
26
|
+
|
27
|
+
def block_open?
|
28
|
+
@blocks.is_a?(Array)
|
29
|
+
end
|
30
|
+
|
31
|
+
def block_code
|
32
|
+
has_block? ? @blocks.join("\n") : ""
|
33
|
+
end
|
34
|
+
|
35
|
+
def block_args
|
36
|
+
YAML.load(block_code)
|
37
|
+
end
|
38
|
+
|
39
|
+
def line_args
|
40
|
+
YAML.load(line_code)
|
41
|
+
end
|
28
42
|
end
|
data/lib/luobo/version.rb
CHANGED
data/luobo.gemspec
CHANGED
@@ -2,12 +2,11 @@ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
|
2
2
|
require 'luobo/version'
|
3
3
|
|
4
4
|
Gem::Specification.new 'luobo', Luobo::VERSION do |s|
|
5
|
-
s.description = "Luobo is
|
6
|
-
s.summary = "Easy to extend code
|
5
|
+
s.description = "Luobo is an easy to extend code generator."
|
6
|
+
s.summary = "Easy to extend macroes inside you soure code"
|
7
7
|
s.authors = ["Huang Wei"]
|
8
8
|
s.email = "huangw@pe-po.com"
|
9
9
|
s.homepage = "https://github.com/huangw/luobo-gem"
|
10
|
-
s.executables << "tuzi"
|
11
10
|
s.files = `git ls-files`.split("\n") - %w[.gitignore]
|
12
11
|
s.test_files = Dir.glob("{spec,test}/**/*.rb")
|
13
12
|
s.rdoc_options = %w[--line-numbers --inline-source --title Luobo --main README.rdoc --encoding=UTF-8]
|
data/spec/block_spec.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class OpLuobo < Luobo
|
4
|
+
attr_accessor :token_stack, :dumps
|
5
|
+
|
6
|
+
def dump contents;
|
7
|
+
# p contents
|
8
|
+
@dumps = Array.new unless @dumps
|
9
|
+
@dumps << contents
|
10
|
+
end
|
11
|
+
|
12
|
+
def last_dump; @dumps[-1] if @dumps end
|
13
|
+
|
14
|
+
def stack_size; @token_stack.size end
|
15
|
+
|
16
|
+
def do_send token
|
17
|
+
"send_out: " + token.line_code + "\n" + token.block_code
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe OpLuobo do
|
22
|
+
|
23
|
+
context "no open block and no indent" do
|
24
|
+
subject {OpLuobo.new('-', STDOUT).process_line(1, "Send out an output")}
|
25
|
+
|
26
|
+
its(:stack_size) { should eq(0) }
|
27
|
+
its(:last_dump) { should eq("Send out an output") }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with an open block and no indent" do
|
31
|
+
subject(:lb) {OpLuobo.new('-', STDOUT).process_line(1, "SEND: out an output ->")}
|
32
|
+
|
33
|
+
its(:stack_size) { should eq(1) }
|
34
|
+
its(:last_dump) { should be_nil }
|
35
|
+
|
36
|
+
it "holds a block line" do
|
37
|
+
lb.process_line(2, " block with indent")
|
38
|
+
lb.stack_size.should eq(1)
|
39
|
+
lb.last_dump.should be_nil
|
40
|
+
|
41
|
+
lb.process_line(3, "This is in the outside")
|
42
|
+
lb.stack_size.should eq(0)
|
43
|
+
lb.last_dump.should eq("This is in the outside")
|
44
|
+
lb.dumps[-2].should eq("send_out: out an output\n block with indent")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with two indented block" do
|
49
|
+
subject(:lb) {OpLuobo.new('-', STDOUT).process_line(1, "SEND: out an output ->")}
|
50
|
+
|
51
|
+
it "holds all block lines" do
|
52
|
+
lb.process_line(2, " block with indent")
|
53
|
+
lb.process_line(3, " block with more indent")
|
54
|
+
lb.process_line(4, "This is in the outside")
|
55
|
+
lb.stack_size.should eq(0)
|
56
|
+
lb.last_dump.should eq("This is in the outside")
|
57
|
+
lb.dumps[-2].should eq("send_out: out an output\n block with indent\n block with more indent")
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with nested indented block" do
|
63
|
+
subject(:lb) {OpLuobo.new('-', STDOUT).process_line(1, "SEND: out an output ->")}
|
64
|
+
|
65
|
+
it "process from the last block" do
|
66
|
+
lb.process_line(2, " block with indent")
|
67
|
+
lb.process_line(3, " SEND: more lines to send ->")
|
68
|
+
lb.process_line(4, " an inside send line")
|
69
|
+
lb.process_line(5, " still in the first block")
|
70
|
+
lb.process_line(6, "close all stack")
|
71
|
+
|
72
|
+
lb.stack_size.should eq(0)
|
73
|
+
lb.last_dump.should eq("close all stack")
|
74
|
+
lb.dumps[-2].should eq("send_out: out an output\n block with indent\nsend_out: more lines to send\n an inside send line\n still in the first block")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class CommandConverter < Luobo
|
4
|
+
def process!
|
5
|
+
"OK"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Luobo do
|
10
|
+
it "exports a class method convert!" do
|
11
|
+
CommandConverter.convert!('examples/hello_processor.rb', STDOUT).should eq("OK")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/spec/loop_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class LoopLuobo < Luobo
|
4
|
+
attr_accessor :token_stack, :dumps
|
5
|
+
|
6
|
+
def regex_line_comment; "\s*#+\s?" end # use # as line comments
|
7
|
+
|
8
|
+
def dump contents;
|
9
|
+
# p contents
|
10
|
+
@dumps = Array.new unless @dumps
|
11
|
+
@dumps << contents
|
12
|
+
end
|
13
|
+
|
14
|
+
def last_dump; @dumps[-1] if @dumps end
|
15
|
+
|
16
|
+
def stack_size; @token_stack.size end
|
17
|
+
|
18
|
+
def do_send token
|
19
|
+
"send_out: " + token.line_code + "\n" + token.block_code
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe LoopLuobo do
|
24
|
+
|
25
|
+
context "Simple examples" do
|
26
|
+
subject(:lb){ LoopLuobo.new('examples/loop_example.rb', STDOUT) }
|
27
|
+
it "expands variables inside" do
|
28
|
+
lb.process!
|
29
|
+
lb.dumps[-3].should eq("var = good!")
|
30
|
+
lb.dumps[-2].should eq("var = also good!")
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class LuaLoopLuobo < Luobo
|
4
|
+
attr_accessor :token_stack, :dumps
|
5
|
+
|
6
|
+
def regex_line_comment; "\s*--+\s?" end # use # as line comments
|
7
|
+
|
8
|
+
def dump contents;
|
9
|
+
# p contents
|
10
|
+
@dumps = Array.new unless @dumps
|
11
|
+
@dumps << contents
|
12
|
+
end
|
13
|
+
|
14
|
+
def last_dump; @dumps[-1] if @dumps end
|
15
|
+
|
16
|
+
def stack_size; @token_stack.size end
|
17
|
+
|
18
|
+
def do_spec token
|
19
|
+
"spec (#{token.line_code}, function()\n" + token.block_code + "\nend)"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe LuaLoopLuobo do
|
24
|
+
|
25
|
+
context "Simple examples" do
|
26
|
+
subject(:lb){ LuaLoopLuobo.new('examples/lua_loop.lua', STDOUT) }
|
27
|
+
it "expands variables inside" do
|
28
|
+
lb.process!
|
29
|
+
lb.dumps[-3].should eq("spec (\"first test\", function()\n local name = \"first\"\n do_test()\nend)")
|
30
|
+
lb.dumps[-2].should eq("spec (\"second test\", function()\n local name = \"last\"\n do_test()\nend)")
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/spec/token_spec.rb
CHANGED
@@ -1,32 +1,123 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class LuaToken < Luobo
|
4
|
+
def regex_line_comment; "\s*\-\-+\s" end
|
5
|
+
def dump contents; contents end # directly return
|
6
|
+
end
|
7
|
+
|
8
|
+
class RubyToken < Luobo
|
9
|
+
def regex_line_comment; "\s*#+\s" end
|
10
|
+
def dump contents; contents end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Luobo do
|
14
|
+
context "without line comment marker" do
|
15
|
+
subject (:lb) do
|
16
|
+
Luobo.new('-', STDOUT)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "return a token without indent" do
|
20
|
+
token = lb.tokenize(1, "PROCNAME: proc line code")
|
21
|
+
token.is_a?(Token).should be_true
|
22
|
+
token.processor_name.should eq("PROCNAME")
|
23
|
+
token.indent_level.should eq(0)
|
24
|
+
token.ln.should eq(1)
|
25
|
+
token.line.should eq("PROCNAME: proc line code")
|
26
|
+
token.line_code.should eq("proc line code")
|
27
|
+
token.has_block?.should be_false
|
28
|
+
token.block_code.should eq("")
|
29
|
+
token.block_open?.should be_false
|
30
|
+
end
|
31
|
+
|
32
|
+
it "return a token with indent" do
|
33
|
+
token = lb.tokenize(1, " PROCNAME: proc line code")
|
34
|
+
token.processor_name.should eq("PROCNAME")
|
35
|
+
token.indent_level.should eq(2)
|
36
|
+
token.line_code.should eq("proc line code")
|
37
|
+
token.has_block?.should be_false
|
38
|
+
token.block_code.should eq("")
|
39
|
+
token.block_open?.should be_false
|
40
|
+
end
|
7
41
|
|
8
|
-
|
9
|
-
|
42
|
+
it "return a token with block open" do
|
43
|
+
token = lb.tokenize(1, "PROCNAME2: proc line code ->")
|
44
|
+
token.processor_name.should eq("PROCNAME2")
|
45
|
+
token.line.should eq("PROCNAME2: proc line code ->")
|
46
|
+
token.line_code.should eq("proc line code")
|
47
|
+
token.block_code.should eq("")
|
48
|
+
token.block_open?.should be_true
|
49
|
+
token.has_block?.should be_false # no block added yet
|
50
|
+
token.add_block_code("Should\nBe True\n")
|
51
|
+
token.has_block?.should be_true
|
52
|
+
token.blocks.size.should eq(2)
|
53
|
+
end
|
10
54
|
end
|
11
55
|
|
12
|
-
|
13
|
-
|
56
|
+
context "lua comment with no indent" do
|
57
|
+
subject do
|
58
|
+
LuaToken.new('-', STDOUT).tokenize(2, " -- SPEC: what ever ->")
|
59
|
+
end
|
60
|
+
|
61
|
+
its :processor_name do
|
62
|
+
should eq("SPEC")
|
63
|
+
end
|
64
|
+
|
65
|
+
its :line_code do
|
66
|
+
should eq("what ever")
|
67
|
+
end
|
68
|
+
|
69
|
+
its :block_open? do
|
70
|
+
should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
its :indent_level do
|
74
|
+
should eq(0)
|
75
|
+
end
|
14
76
|
end
|
15
77
|
|
16
|
-
|
17
|
-
|
78
|
+
|
79
|
+
context "lua comment with indent" do
|
80
|
+
subject do
|
81
|
+
LuaToken.new('-', STDOUT).tokenize(2, " -- SPEC: what ever ->")
|
82
|
+
end
|
83
|
+
|
84
|
+
its :indent_level do
|
85
|
+
should eq(2)
|
86
|
+
end
|
18
87
|
end
|
19
88
|
|
20
|
-
|
21
|
-
|
89
|
+
context "ruby comment with indent" do
|
90
|
+
subject do
|
91
|
+
RubyToken.new('-', STDOUT).tokenize(2, " # INSIDE: what ever ->")
|
92
|
+
end
|
93
|
+
|
94
|
+
its :processor_name do
|
95
|
+
should eq("INSIDE")
|
96
|
+
end
|
97
|
+
|
98
|
+
its :indent_level do
|
99
|
+
should eq(4)
|
100
|
+
end
|
22
101
|
end
|
23
102
|
|
24
|
-
|
25
|
-
|
103
|
+
context "plain raw line" do
|
104
|
+
subject do
|
105
|
+
Luobo.new('-', STDOUT).tokenize(20, "What a wonderful spirit")
|
106
|
+
end
|
107
|
+
|
108
|
+
its :processor_name do
|
109
|
+
should eq("_raw")
|
110
|
+
end
|
26
111
|
end
|
27
112
|
|
28
|
-
|
29
|
-
|
113
|
+
context "ruby comment with raw line" do
|
114
|
+
subject do
|
115
|
+
Luobo.new('-', STDOUT).tokenize(20, "# What a wonderful spirit")
|
116
|
+
end
|
117
|
+
|
118
|
+
its :processor_name do
|
119
|
+
should eq("_raw")
|
120
|
+
end
|
30
121
|
end
|
31
122
|
|
32
123
|
end
|
data/temp.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
def dump contents
|
6
|
+
@output.print contents if contents
|
7
|
+
end
|
8
|
+
|
9
|
+
# travel up through the token stack, close all token with
|
10
|
+
# indent level larger or equal to the parameter
|
11
|
+
def close_stack indent_level
|
12
|
+
source = ''
|
13
|
+
while @token_stack.size > 0 and @token_stack[-1].indent_level >= indent_level
|
14
|
+
if @token_stack.size > 1
|
15
|
+
@token_stack[-2].add_block_code self.convert(@token_stack[-1])
|
16
|
+
else # this is the last token in the stack
|
17
|
+
self.dump(self.convert(@token_stack[-1]))
|
18
|
+
end
|
19
|
+
@token_stack.pop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# add a new line to the existing token if it requires block codes
|
24
|
+
# or write the last token out before a new token
|
25
|
+
# this function invokes after a loop expansion.
|
26
|
+
def process_line line, ln, loop_n = 0
|
27
|
+
indent_level = 0
|
28
|
+
nline = line.gsub(/[\-#\/]/, '')
|
29
|
+
if /^(?<head_space_>\s+)/ =~ nline
|
30
|
+
indent_level = head_space_.size
|
31
|
+
end
|
32
|
+
|
33
|
+
# try to close stack if applicable
|
34
|
+
self.close_stack indent_level
|
35
|
+
|
36
|
+
# starts a named_processor or starts a raw_processor
|
37
|
+
processor_name = '_raw'
|
38
|
+
line_code = line.gsub(/^\s*/,"")
|
39
|
+
block_code = nil
|
40
|
+
if matches = /#{regex_proc_line}/.match(line)
|
41
|
+
proc_head = matches["proc_head_"]
|
42
|
+
processor_name = matches["proc_name_"]
|
43
|
+
line_code = matches["line_code_"]
|
44
|
+
block_code = '' if line_code.gsub!(/#{regex_block_start}/, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
@token_stack << Token.new(ln, line, indent_level, processor_name, line_code, block_code, proc_head)
|
48
|
+
|
49
|
+
# unless it opens for block code close it soon,
|
50
|
+
# (dedicate the dump function to close_stack())
|
51
|
+
self.close_stack indent_level unless block_code
|
52
|
+
end
|
53
|
+
|
54
|
+
# regex definition
|
55
|
+
# -------------------------------
|
56
|
+
def regex_line_comments; "" end # if defined, all
|
57
|
+
|
58
|
+
def regex_proc_head
|
59
|
+
regex_line_comments + '\s*'
|
60
|
+
end
|
61
|
+
|
62
|
+
def regex_proc_name
|
63
|
+
"(?<proc_name_>[A-Z][A-Z0-9_]+)"
|
64
|
+
end
|
65
|
+
|
66
|
+
def regex_proc_end
|
67
|
+
"\s*\:?\s*"
|
68
|
+
end
|
69
|
+
|
70
|
+
def regex_block_start
|
71
|
+
"\s*(?<block_start_>\-\>)?\s*$"
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# ===============================
|
76
|
+
|
77
|
+
# handle convert for each token
|
78
|
+
def convert token
|
79
|
+
pname = "do_" + token.processor_name.downcase
|
80
|
+
if self.respond_to?(pname)
|
81
|
+
self.send(pname.to_sym, token)
|
82
|
+
else
|
83
|
+
self.do__missing(token)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# ===============================
|
88
|
+
|
89
|
+
# parse the file, whenever found a token, convert
|
90
|
+
def process
|
91
|
+
|
92
|
+
fh = File.open(@source_file, 'r:utf-8')
|
93
|
+
self.dump(self.do_setup)
|
94
|
+
|
95
|
+
fh.each do |line|
|
96
|
+
line.chomp!
|
97
|
+
line.gsub!("\t", " ") # convert tab to 2 spaces
|
98
|
+
|
99
|
+
# interrupt for loops
|
100
|
+
if /#{regex_loop_line}/.match(line)
|
101
|
+
in_loop = true
|
102
|
+
@loop_start_ln = $.
|
103
|
+
next # break further processing if detecting a loop
|
104
|
+
end
|
105
|
+
|
106
|
+
# expand and end current loop
|
107
|
+
if in_example
|
108
|
+
unless /#{regex_example_head}/.match(line) # if the line not marked as an example
|
109
|
+
self.expand_loop
|
110
|
+
in_example = false
|
111
|
+
in_loop = false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# end a loop template and start a loop example
|
116
|
+
if in_loop
|
117
|
+
in_example = true if /#{regex_example_head}/.match(line)
|
118
|
+
end
|
119
|
+
|
120
|
+
# dispatch the current line to different holders
|
121
|
+
if in_example
|
122
|
+
self.add_example_line line
|
123
|
+
elsif in_loop
|
124
|
+
@loop_template = @loop_template ? @loop_template + "\n" + line : line
|
125
|
+
else
|
126
|
+
self.process_line line, $.
|
127
|
+
end
|
128
|
+
end
|
129
|
+
fh.close
|
130
|
+
|
131
|
+
# do not forget expand last loop if it reaches over EOF.
|
132
|
+
self.expand_loop if @loop_start_ln > 0
|
133
|
+
|
134
|
+
# close all remain stacks (if any)
|
135
|
+
self.close_stack 0
|
136
|
+
|
137
|
+
self.dump(self.do_cleanup)
|
138
|
+
@output.close if @output_file
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: luobo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -43,27 +43,37 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
description: Luobo is
|
46
|
+
description: Luobo is an easy to extend code generator.
|
47
47
|
email: huangw@pe-po.com
|
48
|
-
executables:
|
49
|
-
- tuzi
|
48
|
+
executables: []
|
50
49
|
extensions: []
|
51
50
|
extra_rdoc_files: []
|
52
51
|
files:
|
53
52
|
- README.rdoc
|
54
53
|
- Rakefile
|
55
|
-
- bin/tuzi
|
56
|
-
- example/parser.lub
|
54
|
+
- backup/bin/tuzi
|
55
|
+
- backup/example/parser.lub
|
56
|
+
- backup/lib/luobo.rb
|
57
|
+
- backup/lib/luobo/base.rb
|
58
|
+
- backup/lib/luobo/driver.rb
|
59
|
+
- backup/lib/luobo/token.rb
|
60
|
+
- backup/lib/luobo/version.rb
|
61
|
+
- backup/spec/driver_spec.rb
|
62
|
+
- backup/spec/parser_spec.rb
|
63
|
+
- backup/spec/token_spec.rb
|
64
|
+
- examples/loop_example.rb
|
65
|
+
- examples/lua_loop.lua
|
57
66
|
- lib/luobo.rb
|
58
|
-
- lib/luobo/base.rb
|
59
|
-
- lib/luobo/driver.rb
|
60
67
|
- lib/luobo/token.rb
|
61
68
|
- lib/luobo/version.rb
|
62
69
|
- luobo.gemspec
|
63
|
-
- spec/
|
64
|
-
- spec/
|
70
|
+
- spec/block_spec.rb
|
71
|
+
- spec/command_spec.rb
|
72
|
+
- spec/loop_spec.rb
|
73
|
+
- spec/lua_loop_spec.rb
|
65
74
|
- spec/spec_helper.rb
|
66
75
|
- spec/token_spec.rb
|
76
|
+
- temp.rb
|
67
77
|
homepage: https://github.com/huangw/luobo-gem
|
68
78
|
licenses: []
|
69
79
|
post_install_message:
|
@@ -94,9 +104,11 @@ rubyforge_project:
|
|
94
104
|
rubygems_version: 1.8.25
|
95
105
|
signing_key:
|
96
106
|
specification_version: 3
|
97
|
-
summary: Easy to extend code
|
107
|
+
summary: Easy to extend macroes inside you soure code
|
98
108
|
test_files:
|
99
|
-
- spec/
|
100
|
-
- spec/
|
109
|
+
- spec/block_spec.rb
|
110
|
+
- spec/command_spec.rb
|
111
|
+
- spec/loop_spec.rb
|
112
|
+
- spec/lua_loop_spec.rb
|
101
113
|
- spec/spec_helper.rb
|
102
114
|
- spec/token_spec.rb
|