mix-language 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/mix-compile +3 -14
- data/lib/javascript/base/array.js +1 -1
- data/lib/javascript/base/env.js +2 -1
- data/lib/javascript/base/eval.js +6 -7
- data/lib/javascript/base/false.js +1 -1
- data/lib/javascript/base/function.js +1 -1
- data/lib/javascript/base/null.js +1 -1
- data/lib/javascript/base/number.js +1 -1
- data/lib/javascript/base/object.js +1 -1
- data/lib/javascript/base/string.js +1 -1
- data/lib/javascript/base/true.js +1 -1
- data/lib/javascript/dom/div.js +1 -1
- data/lib/javascript/dom/drag.js +1 -1
- data/lib/javascript/dom/element.js +1 -1
- data/lib/javascript/dom/event.js +1 -1
- data/lib/javascript/dom/mouse.js +1 -1
- data/lib/javascript/dom/table.js +1 -1
- data/lib/javascript/dom/td.js +1 -1
- data/lib/javascript/dom/text.js +1 -1
- data/lib/javascript/dom/tr.js +1 -1
- data/lib/ruby/compiler.rb +38 -501
- data/lib/ruby/mix.grammar +13 -13
- data/lib/ruby/mix.tab.rb +453 -464
- data/lib/ruby/new_tokenizer.rb +290 -0
- data/lib/ruby/parser.rb +71 -0
- data/lib/ruby/precompiler.rb +106 -0
- data/lib/ruby/tokenizer.rb +268 -0
- data/lib/ruby/translator.rb +179 -0
- metadata +7 -2
@@ -0,0 +1,290 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2011-2012 Jesse Sielaff
|
4
|
+
#
|
5
|
+
|
6
|
+
class MixTokenizer
|
7
|
+
def access?
|
8
|
+
@state == :access
|
9
|
+
end
|
10
|
+
|
11
|
+
def access!
|
12
|
+
@state = :access
|
13
|
+
end
|
14
|
+
|
15
|
+
def access_token
|
16
|
+
add_token '·' + @token
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_token (token, value = token)
|
20
|
+
@token_stream << [token, [value, [@filename, @line]]]
|
21
|
+
end
|
22
|
+
|
23
|
+
def adjust_indent
|
24
|
+
current_indent = @token.length
|
25
|
+
previous_indent = @indent_stack.last
|
26
|
+
|
27
|
+
if current_indent > previous_indent
|
28
|
+
indent_token
|
29
|
+
elsif current_indent < previous_indent
|
30
|
+
unless @indent_stack.include?(current_indent)
|
31
|
+
raise SyntaxError, "Indent error: #{@filename}:#{@line}"
|
32
|
+
end
|
33
|
+
|
34
|
+
outdent_token until @indent_stack.last == current_indent
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def comparison_token
|
39
|
+
add_token @token + '='
|
40
|
+
end
|
41
|
+
|
42
|
+
def double_token?
|
43
|
+
lookahead?(@token) do
|
44
|
+
@token *= 2
|
45
|
+
yield
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def getch
|
50
|
+
@script[@pointer += 1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_token
|
54
|
+
case @token
|
55
|
+
when ?~
|
56
|
+
lookahead?(?~) { skip_to_end_of_line } || skip_whitespace
|
57
|
+
|
58
|
+
when ' ', ?\t
|
59
|
+
no_access!
|
60
|
+
|
61
|
+
when ?\n
|
62
|
+
return if line_empty?
|
63
|
+
|
64
|
+
@token = ''
|
65
|
+
scan_token(/ /)
|
66
|
+
return if lookahead?(?\n) { @empty_lines += 1; unscan }
|
67
|
+
|
68
|
+
newline_token
|
69
|
+
adjust_indent
|
70
|
+
|
71
|
+
when ??, ?., ?;, ?,, ?(, ?{
|
72
|
+
no_access!
|
73
|
+
plain_token
|
74
|
+
|
75
|
+
when ?], ?}, ?)
|
76
|
+
access!
|
77
|
+
plain_token
|
78
|
+
|
79
|
+
when ?[, ?:, ?#
|
80
|
+
(access?) ? access_token : plain_token
|
81
|
+
no_access!
|
82
|
+
|
83
|
+
when ?!, ?=
|
84
|
+
no_access!
|
85
|
+
lookahead?(?=) { comparison_token } || plain_token
|
86
|
+
|
87
|
+
when ?+, ?*, ?/, ?%
|
88
|
+
no_access!
|
89
|
+
lookahead?(?=) { operator_assignment_token } || plain_token
|
90
|
+
|
91
|
+
when ?-
|
92
|
+
return if lookahead?(?=) { operator_assignment_token }
|
93
|
+
return if lookahead?(/[0-9]/) { negative_token; unscan }
|
94
|
+
|
95
|
+
(unary_minus_possible?) ? unary_minus_token : plain_token
|
96
|
+
no_access!
|
97
|
+
|
98
|
+
when ?>, ?<
|
99
|
+
no_access!
|
100
|
+
return if double_token? { lookahead?(?=) { operator_assignment_token } || plain_token }
|
101
|
+
lookahead?(?=) { comparison_token } || plain_token
|
102
|
+
|
103
|
+
when ?&, ?|
|
104
|
+
no_access!
|
105
|
+
return if double_token? { lookahead?(?=) { operator_assignment_token } || plain_token }
|
106
|
+
plain_token
|
107
|
+
|
108
|
+
when /[0-9]/
|
109
|
+
access!
|
110
|
+
scan_token(/\./)
|
111
|
+
scan_token(/[0-9]/)
|
112
|
+
number_token
|
113
|
+
|
114
|
+
when /[A-Z]/
|
115
|
+
no_access!
|
116
|
+
scan_token(/[_a-zA-Z0-9]/)
|
117
|
+
mixin_token
|
118
|
+
|
119
|
+
when ?'
|
120
|
+
access!
|
121
|
+
@token = ""
|
122
|
+
scan_token(/[^']/)
|
123
|
+
getch
|
124
|
+
|
125
|
+
lookahead?(?:) do
|
126
|
+
return if lookahead?(/[^a-z]/) do
|
127
|
+
no_access!
|
128
|
+
key_token
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
string_token
|
133
|
+
|
134
|
+
when /[_a-z]/
|
135
|
+
access!
|
136
|
+
scan_token(/[_a-zA-Z0-9]/)
|
137
|
+
scan_token(/[?!]/)
|
138
|
+
|
139
|
+
lookahead?(?:) do
|
140
|
+
return if lookahead?(/[^a-z]/) do
|
141
|
+
no_access!
|
142
|
+
key_token
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
(keyword?) ? keyword_token : identifier_token
|
147
|
+
|
148
|
+
case @token = getch
|
149
|
+
when ?:, ?#
|
150
|
+
access_token
|
151
|
+
when ?[
|
152
|
+
access_token
|
153
|
+
no_access!
|
154
|
+
else
|
155
|
+
unscan
|
156
|
+
end
|
157
|
+
|
158
|
+
else
|
159
|
+
raise SyntaxError, "Invalid token `#{@token}': #{@filename}:#{@line}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def identifier_token
|
164
|
+
add_token :IDENTIFIER, @token.to_sym
|
165
|
+
end
|
166
|
+
|
167
|
+
def indent_token
|
168
|
+
@indent_stack << @token.length
|
169
|
+
add_token :INDENT
|
170
|
+
end
|
171
|
+
|
172
|
+
def key_token
|
173
|
+
add_token :KEY, @token.to_sym
|
174
|
+
end
|
175
|
+
|
176
|
+
def keyword?
|
177
|
+
%w|app break case
|
178
|
+
elsif else false
|
179
|
+
if null return
|
180
|
+
self switch true
|
181
|
+
unless until while|.include? @token
|
182
|
+
end
|
183
|
+
|
184
|
+
def keyword_token
|
185
|
+
add_token @token.upcase.to_sym
|
186
|
+
end
|
187
|
+
|
188
|
+
def line_empty?
|
189
|
+
if @state == :newline
|
190
|
+
@empty_lines += 1
|
191
|
+
return true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def mixin_token
|
196
|
+
add_token :MIXIN, @token.to_sym
|
197
|
+
end
|
198
|
+
|
199
|
+
def negative_token
|
200
|
+
add_token :NEGATIVE
|
201
|
+
end
|
202
|
+
|
203
|
+
def newline_token
|
204
|
+
add_token :NEWLINE
|
205
|
+
|
206
|
+
@line += (@empty_lines + 1)
|
207
|
+
@empty_lines = 0
|
208
|
+
@state = :newline
|
209
|
+
end
|
210
|
+
|
211
|
+
def lookahead? (pattern)
|
212
|
+
if getch =~ Regexp.new(pattern)
|
213
|
+
yield
|
214
|
+
true
|
215
|
+
else
|
216
|
+
unscan
|
217
|
+
false
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def no_access!
|
222
|
+
@state = :no_access
|
223
|
+
end
|
224
|
+
|
225
|
+
def number_token
|
226
|
+
add_token :NUMBER, @token.to_i
|
227
|
+
end
|
228
|
+
|
229
|
+
def operator_assignment_token
|
230
|
+
add_token '·=', @token
|
231
|
+
end
|
232
|
+
|
233
|
+
def outdent_token
|
234
|
+
@indent_stack.pop
|
235
|
+
add_token :OUTDENT
|
236
|
+
end
|
237
|
+
|
238
|
+
def plain_token
|
239
|
+
add_token @token
|
240
|
+
end
|
241
|
+
|
242
|
+
def scan_token (pattern)
|
243
|
+
while (c = getch) =~ pattern
|
244
|
+
@token << c
|
245
|
+
end
|
246
|
+
|
247
|
+
unscan
|
248
|
+
end
|
249
|
+
|
250
|
+
def skip_to_end_of_line
|
251
|
+
:skip until getch =~ /\n/
|
252
|
+
unscan
|
253
|
+
end
|
254
|
+
|
255
|
+
def skip_whitespace
|
256
|
+
:skip while getch =~ /\s/
|
257
|
+
unscan
|
258
|
+
end
|
259
|
+
|
260
|
+
def string_token
|
261
|
+
add_token :STRING, @token
|
262
|
+
end
|
263
|
+
|
264
|
+
def tokenize (filename, script, token_stream)
|
265
|
+
@filename = filename
|
266
|
+
@script = script
|
267
|
+
@token_stream = token_stream
|
268
|
+
@line = 1
|
269
|
+
@pointer = -1
|
270
|
+
@empty_lines = 0
|
271
|
+
@indent_stack = [0]
|
272
|
+
@state = :newline
|
273
|
+
|
274
|
+
handle_token while @token = getch
|
275
|
+
|
276
|
+
puts @token_stream.inspect
|
277
|
+
end
|
278
|
+
|
279
|
+
def unary_minus_possible?
|
280
|
+
@state == :begin || @state == :newline
|
281
|
+
end
|
282
|
+
|
283
|
+
def unary_minus_token
|
284
|
+
add_token '-·'
|
285
|
+
end
|
286
|
+
|
287
|
+
def unscan
|
288
|
+
@pointer -= 1
|
289
|
+
end
|
290
|
+
end
|
data/lib/ruby/parser.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2011-2012 Jesse Sielaff
|
4
|
+
#
|
5
|
+
|
6
|
+
class MixParser
|
7
|
+
def append_statement (outermost_statement, final_statement)
|
8
|
+
current = outermost_statement
|
9
|
+
|
10
|
+
current = current[:values][1] while current[:values][1]
|
11
|
+
current[:values][1] = final_statement
|
12
|
+
|
13
|
+
outermost_statement
|
14
|
+
end
|
15
|
+
|
16
|
+
def newline (statement_node)
|
17
|
+
statement_node[:values][0] = node(:newline, *@location, statement_node[:values][0])
|
18
|
+
statement_node
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_token
|
22
|
+
@token_stream.shift || [false, false]
|
23
|
+
end
|
24
|
+
|
25
|
+
def node (type, *values)
|
26
|
+
{ type: type, values: values }
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_error (token, value, stack)
|
30
|
+
if value
|
31
|
+
value, file, line = value[0].inspect, *value[1]
|
32
|
+
else
|
33
|
+
value, file, line = 'EOF', *@location
|
34
|
+
end
|
35
|
+
|
36
|
+
raise SyntaxError, "Unexpected token #{token_to_str(token)} (#{value}): #{file}:#{line}".gsub(?",'\"')
|
37
|
+
end
|
38
|
+
|
39
|
+
def parameter (name)
|
40
|
+
@variables[:parameters] << name
|
41
|
+
@variables[:local] << name
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse (tokens)
|
45
|
+
@location = ['',0]
|
46
|
+
@token_stream = tokens
|
47
|
+
@variables = { parameters: [], local: [], embedded: [], prev: nil }
|
48
|
+
|
49
|
+
return do_parse
|
50
|
+
end
|
51
|
+
|
52
|
+
def pop_variables
|
53
|
+
vars = [@variables[:parameters], @variables[:embedded]]
|
54
|
+
@variables = @variables[:prev]
|
55
|
+
|
56
|
+
return vars
|
57
|
+
end
|
58
|
+
|
59
|
+
def push_variables
|
60
|
+
embedded = @variables[:local] + @variables[:embedded]
|
61
|
+
@variables = { parameters: [], local: [], embedded: embedded, prev: @variables }
|
62
|
+
end
|
63
|
+
|
64
|
+
def variable (name)
|
65
|
+
unless (@variables[:local] + @variables[:embedded]).include?(name)
|
66
|
+
@variables[:local] << name
|
67
|
+
end
|
68
|
+
|
69
|
+
node :variable, name
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2011-2012 Jesse Sielaff
|
4
|
+
#
|
5
|
+
require 'strscan'
|
6
|
+
|
7
|
+
class MixPrecompiler
|
8
|
+
JS_BASE_FILES = %w{
|
9
|
+
env eval object array
|
10
|
+
false function null
|
11
|
+
number string true }
|
12
|
+
.each_with_object({}) do |x,h|
|
13
|
+
filename = File.expand_path("../../javascript/base/#{x}.js", __FILE__)
|
14
|
+
h[filename] = File.read(filename)
|
15
|
+
end
|
16
|
+
|
17
|
+
JS_OBJECT_TYPES = %w=
|
18
|
+
ARRAY ELEMENT EVENT
|
19
|
+
FALSE FUNCTION NULL
|
20
|
+
NUMBER OBJECT STRING
|
21
|
+
TEXT TRUE =
|
22
|
+
|
23
|
+
def build_js_string
|
24
|
+
@js_string = @js_files.map {|*,script| script }.join ?\n
|
25
|
+
|
26
|
+
replace_type_variables
|
27
|
+
replace_case_markers
|
28
|
+
minify
|
29
|
+
|
30
|
+
@js_string = <<-JAVASCRIPT
|
31
|
+
mix_result_should_be_undefined=function(){var SYMBOLS='...Replace with symbol table...';#{@js_string};
|
32
|
+
window.onload=function(){startup();evaluate('...Replace with AST nodes...');}}();
|
33
|
+
JAVASCRIPT
|
34
|
+
end
|
35
|
+
|
36
|
+
def collect_scripts
|
37
|
+
@mix_files = {}
|
38
|
+
@js_files = JS_BASE_FILES.dup
|
39
|
+
|
40
|
+
scan_mix_file(@primary_filename)
|
41
|
+
end
|
42
|
+
|
43
|
+
def minify
|
44
|
+
@js_string.gsub!(/\s*\/\*.*?\*\/\s*/m, ?\n)
|
45
|
+
@js_string.gsub!(/\s*\n\s*/, ?\n)
|
46
|
+
@js_string.gsub!(/\s*([{;:,=])\s*/, '\1')
|
47
|
+
@js_string.gsub!("\n}", ?})
|
48
|
+
end
|
49
|
+
|
50
|
+
def precompile (filename)
|
51
|
+
@primary_filename = filename
|
52
|
+
collect_scripts
|
53
|
+
build_js_string
|
54
|
+
|
55
|
+
return @mix_files, @js_string
|
56
|
+
end
|
57
|
+
|
58
|
+
def replace_case_markers
|
59
|
+
MixTranslator::NODE_TYPES.each do |k,v|
|
60
|
+
@js_string.gsub!("case #{k}:","case #{v}:")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def replace_type_variables
|
65
|
+
JS_OBJECT_TYPES.each_with_index do |t,i|
|
66
|
+
@js_string.gsub!("#{t}_TYPE", i.to_s)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def scan_includes (script)
|
71
|
+
ss = StringScanner.new(script)
|
72
|
+
js_filenames = []
|
73
|
+
|
74
|
+
while ss.scan(/~~\s*(.*)\.(js|mix)\s*\n/)
|
75
|
+
basename = ss[1]
|
76
|
+
type = ss[2]
|
77
|
+
filename = "#{basename}.#{type}"
|
78
|
+
|
79
|
+
if type == 'mix'
|
80
|
+
unless [@primary_filename, *@mix_files.keys].include?(filename)
|
81
|
+
scan_mix_file(filename)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
js_filenames << filename
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
js_filenames.each do |name|
|
89
|
+
filename = File.expand_path("../../javascript/#{name}", __FILE__)
|
90
|
+
next if @js_files.has_key?(filename)
|
91
|
+
@js_files[filename] = File.read(filename)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def scan_mix_file (filename)
|
96
|
+
unless File.exists? filename
|
97
|
+
puts "no such file #{filename}"
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
|
101
|
+
script = File.read(filename) + ?\n
|
102
|
+
scan_includes(script)
|
103
|
+
|
104
|
+
@mix_files[filename] = script
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2011-2012 Jesse Sielaff
|
4
|
+
#
|
5
|
+
|
6
|
+
class MixTokenizer
|
7
|
+
def getch
|
8
|
+
@script[@pointer += 1]
|
9
|
+
end
|
10
|
+
|
11
|
+
def token (token, value = token)
|
12
|
+
@tokens << [token, [value, [@filename, @line]]]
|
13
|
+
end
|
14
|
+
|
15
|
+
def tokenize (filename, script, tokens)
|
16
|
+
@filename = filename
|
17
|
+
@script = script
|
18
|
+
@tokens = tokens
|
19
|
+
|
20
|
+
@line = 1
|
21
|
+
@pointer = -1
|
22
|
+
@indent_stack = [0]
|
23
|
+
@state = :newline
|
24
|
+
|
25
|
+
empty_lines = 0
|
26
|
+
|
27
|
+
while c = getch
|
28
|
+
case c
|
29
|
+
when ?~
|
30
|
+
if getch == ?~
|
31
|
+
:skip until getch =~ /\n/
|
32
|
+
unscan
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
unscan
|
37
|
+
:skip while getch =~ /\s/
|
38
|
+
unscan
|
39
|
+
|
40
|
+
when ' ', ?\t
|
41
|
+
@state = :space if @state == :reference
|
42
|
+
|
43
|
+
when ?\n
|
44
|
+
if @state == :newline
|
45
|
+
empty_lines += 1
|
46
|
+
next
|
47
|
+
end
|
48
|
+
|
49
|
+
current_indent = 0
|
50
|
+
previous_indent = @indent_stack.last
|
51
|
+
|
52
|
+
while (c = getch) == ' '
|
53
|
+
current_indent += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
unscan
|
57
|
+
|
58
|
+
if c == ?\n
|
59
|
+
empty_lines += 1
|
60
|
+
next
|
61
|
+
end
|
62
|
+
|
63
|
+
@state = :newline
|
64
|
+
token :NEWLINE
|
65
|
+
|
66
|
+
@line += (empty_lines + 1)
|
67
|
+
empty_lines = 0
|
68
|
+
|
69
|
+
if current_indent > previous_indent
|
70
|
+
@indent_stack << current_indent
|
71
|
+
token :INDENT
|
72
|
+
elsif current_indent < previous_indent
|
73
|
+
unless @indent_stack.include?(current_indent)
|
74
|
+
raise(SyntaxError, "Indent error: #{@filename}:#{@line}")
|
75
|
+
end
|
76
|
+
|
77
|
+
until @indent_stack.last == current_indent
|
78
|
+
@indent_stack.pop
|
79
|
+
token :OUTDENT
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
when ?!, ?=
|
84
|
+
@state = :begin
|
85
|
+
|
86
|
+
if getch == ?=
|
87
|
+
token c + '='
|
88
|
+
else
|
89
|
+
unscan
|
90
|
+
token c
|
91
|
+
end
|
92
|
+
|
93
|
+
when ?+, ?*, ?/, ?%
|
94
|
+
@state = :begin
|
95
|
+
|
96
|
+
if getch == ?=
|
97
|
+
token '·=', c
|
98
|
+
else
|
99
|
+
unscan
|
100
|
+
token c
|
101
|
+
end
|
102
|
+
|
103
|
+
when ?-
|
104
|
+
if getch == ?=
|
105
|
+
token '·=', c
|
106
|
+
elsif c =~ /[0-9]/
|
107
|
+
token :NEGATIVE
|
108
|
+
unscan
|
109
|
+
else
|
110
|
+
unscan
|
111
|
+
|
112
|
+
if @state == :begin || @state == :newline
|
113
|
+
token '-·'
|
114
|
+
else
|
115
|
+
token '-'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
@state = :begin
|
120
|
+
|
121
|
+
when ??, ?., ?;, ?,, ?(, ?{
|
122
|
+
@state = :begin
|
123
|
+
token c
|
124
|
+
|
125
|
+
when ?'
|
126
|
+
@state = :reference
|
127
|
+
|
128
|
+
s = ""
|
129
|
+
|
130
|
+
until (c = getch) == ?'
|
131
|
+
s << c
|
132
|
+
end
|
133
|
+
|
134
|
+
token :STRING, s
|
135
|
+
|
136
|
+
when ?>, ?<
|
137
|
+
@state = :begin
|
138
|
+
|
139
|
+
if getch == c
|
140
|
+
if getch == ?=
|
141
|
+
token '·=', c * 2
|
142
|
+
else
|
143
|
+
unscan
|
144
|
+
token c * 2
|
145
|
+
end
|
146
|
+
else
|
147
|
+
unscan
|
148
|
+
|
149
|
+
if getch == ?=
|
150
|
+
token c + '='
|
151
|
+
else
|
152
|
+
unscan
|
153
|
+
token c
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
when ?&, ?|
|
158
|
+
@state = :begin
|
159
|
+
|
160
|
+
if getch == c
|
161
|
+
if getch == ?=
|
162
|
+
token c + c + '='
|
163
|
+
else
|
164
|
+
unscan
|
165
|
+
token c + c
|
166
|
+
end
|
167
|
+
else
|
168
|
+
unscan
|
169
|
+
token c
|
170
|
+
end
|
171
|
+
|
172
|
+
when /[0-9]/
|
173
|
+
@state = :reference
|
174
|
+
value = c
|
175
|
+
|
176
|
+
while (c = getch) =~ /[0-9]/
|
177
|
+
value << c
|
178
|
+
end
|
179
|
+
|
180
|
+
unscan
|
181
|
+
|
182
|
+
token :NUMBER, value.to_i
|
183
|
+
|
184
|
+
when ?], ?}, ?)
|
185
|
+
@state = :reference
|
186
|
+
token c
|
187
|
+
|
188
|
+
when ?:, ?[, ?#
|
189
|
+
if @state == :reference
|
190
|
+
token '·' + c
|
191
|
+
else
|
192
|
+
token c
|
193
|
+
end
|
194
|
+
|
195
|
+
@state = :begin
|
196
|
+
|
197
|
+
when /[A-Z]/
|
198
|
+
@state = :begin
|
199
|
+
value = c
|
200
|
+
|
201
|
+
while (c = getch) =~ /[_a-zA-Z0-9]/
|
202
|
+
value << c
|
203
|
+
end
|
204
|
+
|
205
|
+
unscan
|
206
|
+
|
207
|
+
token :MIXIN, value.to_sym
|
208
|
+
|
209
|
+
when /[_a-z]/
|
210
|
+
@state = :reference
|
211
|
+
value = c
|
212
|
+
|
213
|
+
while (c = getch) =~ /[_a-zA-Z0-9]/
|
214
|
+
value << c
|
215
|
+
end
|
216
|
+
|
217
|
+
if c == ?:
|
218
|
+
if getch =~ /[a-z]/
|
219
|
+
unscan
|
220
|
+
else
|
221
|
+
@state = :begin
|
222
|
+
token :KEY, value.to_sym
|
223
|
+
unscan
|
224
|
+
next
|
225
|
+
end
|
226
|
+
|
227
|
+
unscan
|
228
|
+
elsif c == ?? || c == ?!
|
229
|
+
value << c
|
230
|
+
else
|
231
|
+
unscan
|
232
|
+
end
|
233
|
+
|
234
|
+
if %w:
|
235
|
+
app break case
|
236
|
+
elsif else false if
|
237
|
+
null return self
|
238
|
+
switch true unless
|
239
|
+
until while
|
240
|
+
:
|
241
|
+
.include?(value)
|
242
|
+
token value.upcase.to_sym
|
243
|
+
else
|
244
|
+
token :IDENTIFIER, value.to_sym
|
245
|
+
end
|
246
|
+
|
247
|
+
case c = getch
|
248
|
+
when ?:, ?#
|
249
|
+
token '·' + c
|
250
|
+
when ?[
|
251
|
+
token '·['
|
252
|
+
@state = :begin
|
253
|
+
else
|
254
|
+
unscan
|
255
|
+
end
|
256
|
+
|
257
|
+
else
|
258
|
+
raise SyntaxError, "Invalid token `#{c}': #{@filename}:#{@line}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
@tokens
|
263
|
+
end
|
264
|
+
|
265
|
+
def unscan
|
266
|
+
@pointer -= 1
|
267
|
+
end
|
268
|
+
end
|