luobo 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|