handlebars 0.0.2 → 0.2.0

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.
Files changed (47) hide show
  1. data/.gitignore +1 -1
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +1 -1
  5. data/README.mdown +44 -0
  6. data/Rakefile +3 -0
  7. data/handlebars.gemspec +19 -13
  8. data/lib/handlebars.rb +4 -3
  9. data/lib/handlebars/context.rb +37 -0
  10. data/lib/handlebars/version.rb +1 -1
  11. data/spec/handlebars_spec.rb +40 -0
  12. data/spike.rb +17 -0
  13. data/vendor/handlebars/.gitignore +6 -0
  14. data/vendor/handlebars/.jshintrc +50 -0
  15. data/vendor/handlebars/.npmignore +11 -0
  16. data/vendor/handlebars/Gemfile +5 -0
  17. data/vendor/handlebars/LICENSE +20 -0
  18. data/vendor/handlebars/README.markdown +315 -0
  19. data/vendor/handlebars/Rakefile +116 -0
  20. data/vendor/handlebars/bench/benchwarmer.js +149 -0
  21. data/vendor/handlebars/bench/handlebars.js +163 -0
  22. data/vendor/handlebars/bin/handlebars +139 -0
  23. data/vendor/handlebars/lib/handlebars.js +14 -0
  24. data/vendor/handlebars/lib/handlebars/base.js +101 -0
  25. data/vendor/handlebars/lib/handlebars/compiler/ast.js +103 -0
  26. data/vendor/handlebars/lib/handlebars/compiler/base.js +27 -0
  27. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +808 -0
  28. data/vendor/handlebars/lib/handlebars/compiler/index.js +7 -0
  29. data/vendor/handlebars/lib/handlebars/compiler/printer.js +137 -0
  30. data/vendor/handlebars/lib/handlebars/compiler/visitor.js +13 -0
  31. data/vendor/handlebars/lib/handlebars/runtime.js +68 -0
  32. data/vendor/handlebars/lib/handlebars/utils.js +68 -0
  33. data/vendor/handlebars/package.json +25 -0
  34. data/vendor/handlebars/spec/acceptance_spec.rb +101 -0
  35. data/vendor/handlebars/spec/parser_spec.rb +264 -0
  36. data/vendor/handlebars/spec/qunit_spec.js +1067 -0
  37. data/vendor/handlebars/spec/spec_helper.rb +157 -0
  38. data/vendor/handlebars/spec/tokenizer_spec.rb +254 -0
  39. data/vendor/handlebars/src/handlebars.l +42 -0
  40. data/vendor/handlebars/src/handlebars.yy +99 -0
  41. metadata +93 -77
  42. data/README.md +0 -39
  43. data/lib/handlebars/generator.rb +0 -4
  44. data/lib/handlebars/parser.rb +0 -240
  45. data/spec/generator_spec.rb +0 -5
  46. data/spec/parser_spec.rb +0 -163
  47. data/spec/spec_helper.rb +0 -17
@@ -0,0 +1,157 @@
1
+ require "v8"
2
+
3
+ # Monkey patches due to bugs in RubyRacer
4
+ class V8::JSError
5
+ def initialize(try, to)
6
+ @to = to
7
+ begin
8
+ super(initialize_unsafe(try))
9
+ rescue Exception => e
10
+ # Original code does not make an Array here
11
+ @boundaries = [Boundary.new(:rbframes => e.backtrace)]
12
+ @value = e
13
+ super("BUG! please report. JSError#initialize failed!: #{e.message}")
14
+ end
15
+ end
16
+
17
+ def parse_js_frames(try)
18
+ raw = @to.rb(try.StackTrace())
19
+ if raw && !raw.empty?
20
+ raw.split("\n")[1..-1].tap do |frames|
21
+ # Original code uses strip!, and the frames are not guaranteed to be strippable
22
+ frames.each {|frame| frame.strip.chomp!(",")}
23
+ end
24
+ else
25
+ []
26
+ end
27
+ end
28
+ end
29
+
30
+ module Handlebars
31
+ module Spec
32
+ def self.js_backtrace(context)
33
+ begin
34
+ context.eval("throw")
35
+ rescue V8::JSError => e
36
+ return e.backtrace(:javascript)
37
+ end
38
+ end
39
+
40
+ def self.remove_exports(string)
41
+ match = string.match(%r{\A(.*?)^// BEGIN\(BROWSER\)\n(.*)\n^// END\(BROWSER\)(.*?)\Z}m)
42
+ prelines = match ? match[1].count("\n") + 1 : 0
43
+ ret = match ? match[2] : string
44
+ ("\n" * prelines) + ret
45
+ end
46
+
47
+ def self.load_helpers(context)
48
+ context["exports"] = nil
49
+
50
+ context["p"] = proc do |val|
51
+ p val if ENV["DEBUG_JS"]
52
+ end
53
+
54
+ context["puts"] = proc do |val|
55
+ puts val if ENV["DEBUG_JS"]
56
+ end
57
+
58
+ context["puts_node"] = proc do |val|
59
+ puts context["Handlebars"]["PrintVisitor"].new.accept(val)
60
+ puts
61
+ end
62
+
63
+ context["puts_caller"] = proc do
64
+ puts "BACKTRACE:"
65
+ puts Handlebars::Spec.js_backtrace(context)
66
+ puts
67
+ end
68
+ end
69
+
70
+ def self.js_load(context, file)
71
+ str = File.read(file)
72
+ context.eval(remove_exports(str), file)
73
+ end
74
+
75
+ CONTEXT = V8::Context.new
76
+ CONTEXT.instance_eval do |context|
77
+ Handlebars::Spec.load_helpers(context);
78
+
79
+ Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
80
+ Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
81
+ Handlebars::Spec.js_load(context, 'lib/handlebars/runtime.js');
82
+
83
+ context["CompilerContext"] = {}
84
+ CompilerContext = context["CompilerContext"]
85
+ CompilerContext["compile"] = proc do |*args|
86
+ template, options = args[0], args[1] || nil
87
+ templateSpec = COMPILE_CONTEXT["Handlebars"]["precompile"].call(template, options);
88
+ context["Handlebars"]["template"].call(context.eval("(#{templateSpec})"));
89
+ end
90
+ CompilerContext["compileWithPartial"] = proc do |*args|
91
+ template, options = args[0], args[1] || nil
92
+ FULL_CONTEXT["Handlebars"]["compile"].call(template, options);
93
+ end
94
+ end
95
+
96
+ COMPILE_CONTEXT = V8::Context.new
97
+ COMPILE_CONTEXT.instance_eval do |context|
98
+ Handlebars::Spec.load_helpers(context);
99
+
100
+ Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
101
+ Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
102
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/parser.js');
103
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/base.js');
104
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/ast.js');
105
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/visitor.js');
106
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/printer.js');
107
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/compiler.js');
108
+
109
+ context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
110
+
111
+ context["Handlebars"]["logger"]["log"] = proc do |level, str|
112
+ logger_level = context["Handlebars"]["logger"]["level"].to_i
113
+
114
+ if logger_level <= level
115
+ puts str
116
+ end
117
+ end
118
+ end
119
+
120
+ FULL_CONTEXT = V8::Context.new
121
+ FULL_CONTEXT.instance_eval do |context|
122
+ Handlebars::Spec.load_helpers(context);
123
+
124
+ Handlebars::Spec.js_load(context, 'lib/handlebars/base.js');
125
+ Handlebars::Spec.js_load(context, 'lib/handlebars/utils.js');
126
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/parser.js');
127
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/base.js');
128
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/ast.js');
129
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/visitor.js');
130
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/printer.js');
131
+ Handlebars::Spec.js_load(context, 'lib/handlebars/compiler/compiler.js');
132
+ Handlebars::Spec.js_load(context, 'lib/handlebars/runtime.js');
133
+
134
+ context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
135
+
136
+ context["Handlebars"]["logger"]["log"] = proc do |level, str|
137
+ logger_level = context["Handlebars"]["logger"]["level"].to_i
138
+
139
+ if logger_level <= level
140
+ puts str
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+
148
+ require "test/unit/assertions"
149
+
150
+ RSpec.configure do |config|
151
+ config.include Test::Unit::Assertions
152
+
153
+ # Each is required to allow classes to mark themselves as compiler tests
154
+ config.before(:each) do
155
+ @context = @compiles ? Handlebars::Spec::COMPILE_CONTEXT : Handlebars::Spec::CONTEXT
156
+ end
157
+ end
@@ -0,0 +1,254 @@
1
+ require "spec_helper"
2
+ require "timeout"
3
+
4
+ describe "Tokenizer" do
5
+ let(:parser) { @context["handlebars"] }
6
+ let(:lexer) { @context["handlebars"]["lexer"] }
7
+
8
+ before(:all) do
9
+ @compiles = true
10
+ end
11
+ Token = Struct.new(:name, :text)
12
+
13
+ def tokenize(string)
14
+ lexer.setInput(string)
15
+ out = []
16
+
17
+ while token = lexer.lex
18
+ # p token
19
+ result = parser.terminals_[token] || token
20
+ # p result
21
+ break if !result || result == "EOF" || result == "INVALID"
22
+ out << Token.new(result, lexer.yytext)
23
+ end
24
+
25
+ out
26
+ end
27
+
28
+ RSpec::Matchers.define :match_tokens do |tokens|
29
+ match do |result|
30
+ result.map(&:name).should == tokens
31
+ end
32
+ end
33
+
34
+ RSpec::Matchers.define :be_token do |name, string|
35
+ match do |token|
36
+ token.name.should == name
37
+ token.text.should == string
38
+ end
39
+ end
40
+
41
+ it "tokenizes a simple mustache as 'OPEN ID CLOSE'" do
42
+ result = tokenize("{{foo}}")
43
+ result.should match_tokens(%w(OPEN ID CLOSE))
44
+ result[1].should be_token("ID", "foo")
45
+ end
46
+
47
+ it "supports escaping delimiters" do
48
+ result = tokenize("{{foo}} \\{{bar}} {{baz}}")
49
+ result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT OPEN ID CLOSE))
50
+
51
+ result[4].should be_token("CONTENT", "{{bar}} ")
52
+ end
53
+
54
+ it "supports escaping a triple stash" do
55
+ result = tokenize("{{foo}} \\{{{bar}}} {{baz}}")
56
+ result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT OPEN ID CLOSE))
57
+
58
+ result[4].should be_token("CONTENT", "{{{bar}}} ")
59
+ end
60
+
61
+ it "tokenizes a simple path" do
62
+ result = tokenize("{{foo/bar}}")
63
+ result.should match_tokens(%w(OPEN ID SEP ID CLOSE))
64
+ end
65
+
66
+ it "allows dot notation" do
67
+ result = tokenize("{{foo.bar}}")
68
+ result.should match_tokens(%w(OPEN ID SEP ID CLOSE))
69
+
70
+ tokenize("{{foo.bar.baz}}").should match_tokens(%w(OPEN ID SEP ID SEP ID CLOSE))
71
+ end
72
+
73
+ it "allows path literals with []" do
74
+ result = tokenize("{{foo.[bar]}}")
75
+ result.should match_tokens(%w(OPEN ID SEP ID CLOSE))
76
+ end
77
+
78
+ it "allows multiple path literals on a line with []" do
79
+ result = tokenize("{{foo.[bar]}}{{foo.[baz]}}")
80
+ result.should match_tokens(%w(OPEN ID SEP ID CLOSE OPEN ID SEP ID CLOSE))
81
+ end
82
+
83
+ it "tokenizes {{.}} as OPEN ID CLOSE" do
84
+ result = tokenize("{{.}}")
85
+ result.should match_tokens(%w(OPEN ID CLOSE))
86
+ end
87
+
88
+ it "tokenizes a path as 'OPEN (ID SEP)* ID CLOSE'" do
89
+ result = tokenize("{{../foo/bar}}")
90
+ result.should match_tokens(%w(OPEN ID SEP ID SEP ID CLOSE))
91
+ result[1].should be_token("ID", "..")
92
+ end
93
+
94
+ it "tokenizes a path with .. as a parent path" do
95
+ result = tokenize("{{../foo.bar}}")
96
+ result.should match_tokens(%w(OPEN ID SEP ID SEP ID CLOSE))
97
+ result[1].should be_token("ID", "..")
98
+ end
99
+
100
+ it "tokenizes a path with this/foo as OPEN ID SEP ID CLOSE" do
101
+ result = tokenize("{{this/foo}}")
102
+ result.should match_tokens(%w(OPEN ID SEP ID CLOSE))
103
+ result[1].should be_token("ID", "this")
104
+ result[3].should be_token("ID", "foo")
105
+ end
106
+
107
+ it "tokenizes a simple mustache with spaces as 'OPEN ID CLOSE'" do
108
+ result = tokenize("{{ foo }}")
109
+ result.should match_tokens(%w(OPEN ID CLOSE))
110
+ result[1].should be_token("ID", "foo")
111
+ end
112
+
113
+ it "tokenizes a simple mustache with line breaks as 'OPEN ID ID CLOSE'" do
114
+ result = tokenize("{{ foo \n bar }}")
115
+ result.should match_tokens(%w(OPEN ID ID CLOSE))
116
+ result[1].should be_token("ID", "foo")
117
+ end
118
+
119
+ it "tokenizes raw content as 'CONTENT'" do
120
+ result = tokenize("foo {{ bar }} baz")
121
+ result.should match_tokens(%w(CONTENT OPEN ID CLOSE CONTENT))
122
+ result[0].should be_token("CONTENT", "foo ")
123
+ result[4].should be_token("CONTENT", " baz")
124
+ end
125
+
126
+ it "tokenizes a partial as 'OPEN_PARTIAL ID CLOSE'" do
127
+ result = tokenize("{{> foo}}")
128
+ result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
129
+ end
130
+
131
+ it "tokenizes a partial with context as 'OPEN_PARTIAL ID ID CLOSE'" do
132
+ result = tokenize("{{> foo bar }}")
133
+ result.should match_tokens(%w(OPEN_PARTIAL ID ID CLOSE))
134
+ end
135
+
136
+ it "tokenizes a partial without spaces as 'OPEN_PARTIAL ID CLOSE'" do
137
+ result = tokenize("{{>foo}}")
138
+ result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
139
+ end
140
+
141
+ it "tokenizes a partial space at the end as 'OPEN_PARTIAL ID CLOSE'" do
142
+ result = tokenize("{{>foo }}")
143
+ result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
144
+ end
145
+
146
+ it "tokenizes a comment as 'COMMENT'" do
147
+ result = tokenize("foo {{! this is a comment }} bar {{ baz }}")
148
+ result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE))
149
+ result[1].should be_token("COMMENT", " this is a comment ")
150
+ end
151
+
152
+ it "tokenizes open and closing blocks as 'OPEN_BLOCK ID CLOSE ... OPEN_ENDBLOCK ID CLOSE'" do
153
+ result = tokenize("{{#foo}}content{{/foo}}")
154
+ result.should match_tokens(%w(OPEN_BLOCK ID CLOSE CONTENT OPEN_ENDBLOCK ID CLOSE))
155
+ end
156
+
157
+ it "tokenizes inverse sections as 'OPEN_INVERSE CLOSE'" do
158
+ tokenize("{{^}}").should match_tokens(%w(OPEN_INVERSE CLOSE))
159
+ tokenize("{{else}}").should match_tokens(%w(OPEN_INVERSE CLOSE))
160
+ tokenize("{{ else }}").should match_tokens(%w(OPEN_INVERSE CLOSE))
161
+ end
162
+
163
+ it "tokenizes inverse sections with ID as 'OPEN_INVERSE ID CLOSE'" do
164
+ result = tokenize("{{^foo}}")
165
+ result.should match_tokens(%w(OPEN_INVERSE ID CLOSE))
166
+ result[1].should be_token("ID", "foo")
167
+ end
168
+
169
+ it "tokenizes inverse sections with ID and spaces as 'OPEN_INVERSE ID CLOSE'" do
170
+ result = tokenize("{{^ foo }}")
171
+ result.should match_tokens(%w(OPEN_INVERSE ID CLOSE))
172
+ result[1].should be_token("ID", "foo")
173
+ end
174
+
175
+ it "tokenizes mustaches with params as 'OPEN ID ID ID CLOSE'" do
176
+ result = tokenize("{{ foo bar baz }}")
177
+ result.should match_tokens(%w(OPEN ID ID ID CLOSE))
178
+ result[1].should be_token("ID", "foo")
179
+ result[2].should be_token("ID", "bar")
180
+ result[3].should be_token("ID", "baz")
181
+ end
182
+
183
+ it "tokenizes mustaches with String params as 'OPEN ID ID STRING CLOSE'" do
184
+ result = tokenize("{{ foo bar \"baz\" }}")
185
+ result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
186
+ result[3].should be_token("STRING", "baz")
187
+ end
188
+
189
+ it "tokenizes String params with spaces inside as 'STRING'" do
190
+ result = tokenize("{{ foo bar \"baz bat\" }}")
191
+ result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
192
+ result[3].should be_token("STRING", "baz bat")
193
+ end
194
+
195
+ it "tokenizes String params with escapes quotes as 'STRING'" do
196
+ result = tokenize(%|{{ foo "bar\\"baz" }}|)
197
+ result.should match_tokens(%w(OPEN ID STRING CLOSE))
198
+ result[2].should be_token("STRING", %{bar"baz})
199
+ end
200
+
201
+ it "tokenizes numbers" do
202
+ result = tokenize(%|{{ foo 1 }}|)
203
+ result.should match_tokens(%w(OPEN ID INTEGER CLOSE))
204
+ result[2].should be_token("INTEGER", "1")
205
+ end
206
+
207
+ it "tokenizes booleans" do
208
+ result = tokenize(%|{{ foo true }}|)
209
+ result.should match_tokens(%w(OPEN ID BOOLEAN CLOSE))
210
+ result[2].should be_token("BOOLEAN", "true")
211
+
212
+ result = tokenize(%|{{ foo false }}|)
213
+ result.should match_tokens(%w(OPEN ID BOOLEAN CLOSE))
214
+ result[2].should be_token("BOOLEAN", "false")
215
+ end
216
+
217
+ it "tokenizes hash arguments" do
218
+ result = tokenize("{{ foo bar=baz }}")
219
+ result.should match_tokens %w(OPEN ID ID EQUALS ID CLOSE)
220
+
221
+ result = tokenize("{{ foo bar baz=bat }}")
222
+ result.should match_tokens %w(OPEN ID ID ID EQUALS ID CLOSE)
223
+
224
+ result = tokenize("{{ foo bar baz=1 }}")
225
+ result.should match_tokens %w(OPEN ID ID ID EQUALS INTEGER CLOSE)
226
+
227
+ result = tokenize("{{ foo bar baz=true }}")
228
+ result.should match_tokens %w(OPEN ID ID ID EQUALS BOOLEAN CLOSE)
229
+
230
+ result = tokenize("{{ foo bar baz=false }}")
231
+ result.should match_tokens %w(OPEN ID ID ID EQUALS BOOLEAN CLOSE)
232
+
233
+ result = tokenize("{{ foo bar\n baz=bat }}")
234
+ result.should match_tokens %w(OPEN ID ID ID EQUALS ID CLOSE)
235
+
236
+ result = tokenize("{{ foo bar baz=\"bat\" }}")
237
+ result.should match_tokens %w(OPEN ID ID ID EQUALS STRING CLOSE)
238
+
239
+ result = tokenize("{{ foo bar baz=\"bat\" bam=wot }}")
240
+ result.should match_tokens %w(OPEN ID ID ID EQUALS STRING ID EQUALS ID CLOSE)
241
+
242
+ result = tokenize("{{foo omg bar=baz bat=\"bam\"}}")
243
+ result.should match_tokens %w(OPEN ID ID ID EQUALS ID ID EQUALS STRING CLOSE)
244
+ result[2].should be_token("ID", "omg")
245
+ end
246
+
247
+ it "does not time out in a mustache with a single } followed by EOF" do
248
+ Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
249
+ end
250
+
251
+ it "does not time out in a mustache when invalid ID characters are used" do
252
+ Timeout.timeout(1) { tokenize("{{foo & }}").should match_tokens(%w(OPEN ID)) }
253
+ end
254
+ end
@@ -0,0 +1,42 @@
1
+
2
+ %x mu emu
3
+
4
+ %%
5
+
6
+ [^\x00]*?/("{{") {
7
+ if(yytext.slice(-1) !== "\\") this.begin("mu");
8
+ if(yytext.slice(-1) === "\\") yytext = yytext.substr(0,yyleng-1), this.begin("emu");
9
+ if(yytext) return 'CONTENT';
10
+ }
11
+
12
+ [^\x00]+ { return 'CONTENT'; }
13
+
14
+ <emu>[^\x00]{2,}?/("{{") { this.popState(); return 'CONTENT'; }
15
+
16
+ <mu>"{{>" { return 'OPEN_PARTIAL'; }
17
+ <mu>"{{#" { return 'OPEN_BLOCK'; }
18
+ <mu>"{{/" { return 'OPEN_ENDBLOCK'; }
19
+ <mu>"{{^" { return 'OPEN_INVERSE'; }
20
+ <mu>"{{"\s*"else" { return 'OPEN_INVERSE'; }
21
+ <mu>"{{{" { return 'OPEN_UNESCAPED'; }
22
+ <mu>"{{&" { return 'OPEN_UNESCAPED'; }
23
+ <mu>"{{!"[\s\S]*?"}}" { yytext = yytext.substr(3,yyleng-5); this.popState(); return 'COMMENT'; }
24
+ <mu>"{{" { return 'OPEN'; }
25
+
26
+ <mu>"=" { return 'EQUALS'; }
27
+ <mu>"."/[} ] { return 'ID'; }
28
+ <mu>".." { return 'ID'; }
29
+ <mu>[\/.] { return 'SEP'; }
30
+ <mu>\s+ { /*ignore whitespace*/ }
31
+ <mu>"}}}" { this.popState(); return 'CLOSE'; }
32
+ <mu>"}}" { this.popState(); return 'CLOSE'; }
33
+ <mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
34
+ <mu>"true"/[}\s] { return 'BOOLEAN'; }
35
+ <mu>"false"/[}\s] { return 'BOOLEAN'; }
36
+ <mu>[0-9]+/[}\s] { return 'INTEGER'; }
37
+ <mu>[a-zA-Z0-9_$-]+/[=}\s\/.] { return 'ID'; }
38
+ <mu>'['[^\]]*']' { yytext = yytext.substr(1, yyleng-2); return 'ID'; }
39
+ <mu>. { return 'INVALID'; }
40
+
41
+ <INITIAL,mu><<EOF>> { return 'EOF'; }
42
+