luobo 0.0.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,36 @@
1
1
  = Luobo
2
2
 
3
- Luobo is a simple, easy to extend code generator.
3
+ Luobo is a code-generator that can expands macros defined in a source file.
4
4
 
5
- When you feed <tt>tuzi</tt> (bunny) with some luobo (carrot) script, it will produce sources base on your predefined rules.
5
+ Basically you use luobo by extends a Converter:
6
6
 
7
- = Examples
7
+ require "luobo"
8
+ class LuaSpecConverter < Luobo::Converter
9
+ def regex_comment_header; '^\s*--' end
8
10
 
9
- = Build-in Drivers
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
File without changes
@@ -12,10 +12,10 @@ Raw line contains ->
12
12
  AST: Raw line contains -> ->
13
13
  AST: 2,fake block
14
14
 
15
- ## SPEC: a spec blog ->
15
+ SPEC: a spec blog ->
16
16
  do that
17
17
  do this
18
- ## SPEC: nested spec ->
18
+ SPEC: nested spec ->
19
19
  redo that
20
20
  redo this
21
21
  doing outside
@@ -0,0 +1,5 @@
1
+ require "yaml"
2
+
3
+ require "luobo/token"
4
+ require "luobo/driver"
5
+ require "luobo/base"
@@ -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
- "\s*(\#{2,}|\-{2,}|\/{2,})?\s*"
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
@@ -0,0 +1,4 @@
1
+ module Luobo
2
+ VERSION = '0.0.3'
3
+ end
4
+
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
@@ -0,0 +1,8 @@
1
+ # This is a loop example.
2
+
3
+ #% ====
4
+ var = <%= variable %>
5
+ #% ----
6
+ #% variable: good!
7
+ #% ----
8
+ #% variable: also good!
@@ -0,0 +1,11 @@
1
+
2
+ -- % ====
3
+ -- SPEC: "<%= test_name %>" ->
4
+ local name = "<%= name %>"
5
+ do_test()
6
+ -- % ----
7
+ -- % test_name: first test
8
+ -- % name: first
9
+ -- % ----
10
+ -- % test_name: second test
11
+ -- % name: last
@@ -1,5 +1,221 @@
1
1
  require "yaml"
2
-
2
+ require "erubis"
3
3
  require "luobo/token"
4
- require "luobo/driver"
5
- require "luobo/base"
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
@@ -1,28 +1,42 @@
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
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
- def initialize ln, line, indent_level, processor_name, line_code, block_code
7
- @ln, @line, @indent_level, @processor_name, @line_code, @block_code = ln, line, indent_level, processor_name, line_code, block_code
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
- 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
- def block_args
21
- YAML.load(block_code)
22
- end
11
+ def indent
12
+ " " * indent_level
13
+ end
23
14
 
24
- def line_args
25
- YAML.load(line_code)
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
@@ -1,4 +1,4 @@
1
1
  module Luobo
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.5'
3
3
  end
4
4
 
@@ -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 a simple, easy to extend code generator."
6
- s.summary = "Easy to extend code generator"
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]
@@ -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
+
@@ -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
@@ -1,32 +1,123 @@
1
1
  require "spec_helper"
2
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
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
- its :ln do
9
- should eq(1)
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
- its :line do
13
- should eq("line")
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
- its :indent_level do
17
- should eq(4)
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
- its :processor_name do
21
- should eq("Echo")
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
- its :line_args do
25
- token.line_args["same"].should eq("hash")
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
- its :block_args do
29
- token.block_args[2].should eq("array")
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.2
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-03-28 00:00:00.000000000 Z
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 a simple, easy to extend code generator.
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/driver_spec.rb
64
- - spec/parser_spec.rb
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 generator
107
+ summary: Easy to extend macroes inside you soure code
98
108
  test_files:
99
- - spec/driver_spec.rb
100
- - spec/parser_spec.rb
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