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.
@@ -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