vinter 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/lib/vinter/ast_printer.rb +14 -0
- data/lib/vinter/lexer.rb +132 -38
- data/lib/vinter/parser.rb +471 -640
- data/lib/vinter.rb +2 -1
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 617e3da0fed64bcf718e3c1e3bd82a66e910e4a2099a27942a2390c93421779b
|
|
4
|
+
data.tar.gz: a54306cbf4b2e25921c3518985acb0d648096a66e1f24a86e59ac3aa53d56810
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 614e2e7ef3f02f16a8888069c40244761a7c3afb142b84aafb410c39693e25d4e3d9afa9e4a8bafea24512dc9b339364bf8e0ace1a665d424d0f638428d4b7f9
|
|
7
|
+
data.tar.gz: 8ace7e570a66a113e66c411d5725f1a72fc3596faffcda899e3590b37b7893aaff21aeefc4d1e1e60fa2cc20f830377417c493b88af4ab90f126f72b843ee521
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Vinter
|
|
2
|
+
module ASTPrinter
|
|
3
|
+
def self.print(ast, indent=0)
|
|
4
|
+
spaces = " " * indent * 2
|
|
5
|
+
ast[:body].each do |node|
|
|
6
|
+
puts "#{spaces}(#{node[:type]}|#{node[:value]})"
|
|
7
|
+
if node[:type] == :export_statement
|
|
8
|
+
puts "(#{node[:export][:type]})"
|
|
9
|
+
print(node[:export], indent=1)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/vinter/lexer.rb
CHANGED
|
@@ -2,7 +2,10 @@ module Vinter
|
|
|
2
2
|
class Lexer
|
|
3
3
|
TOKEN_TYPES = {
|
|
4
4
|
# Vim9 specific keywords
|
|
5
|
-
keyword: /\b(if|else|elseif|endif|while|endwhile|for|endfor|def|enddef|function|endfunction|endfunc|return|const|var|final|import|export|class|extends|static|enum|type|vim9script|abort|autocmd|echom|echoerr|echohl|echomsg|let|unlet|execute|exec|continue|break|try|catch|finally|endtry|throw|runtime|silent|delete|command|call|set|setlocal|syntax|
|
|
5
|
+
keyword: /\b(if|else|elseif|endif|while|endwhile|for|endfor|def|enddef|function|endfunction|endfunc|return|const|var|final|import|export|class|extends|static|enum|type|vim9script|scriptencoding|abort|autocmd|echom|echoerr|echohl|echomsg|let|unlet|execute|exec|continue|break|try|catch|finally|endtry|throw|runtime|silent|delete|command|call|set|setlocal|syntax|sleep|source|nnoremap|nmap|inoremap|imap|vnoremap|vmap|xnoremap|xmap|cnoremap|cmap|noremap|map|var)\b/,
|
|
6
|
+
encodings: /\b(latin1|iso|koi8|macroman|cp437|cp737|cp775|cp850|cp852|cp855|cp857|cp860|cp861|cp862|cp863|cp865|cp866|cp869|cp874|cp1250|cp1251|cp1253|cp1254|cp1255|cp1256|cp1257|cp1258|cp932|euc\-jp|sjis|cp949|euc\-kr|cp936|euc\-cn|cp950|big5|euc\-tw|utf\-8|ucs\-2|ucs\-21e|utf\-16|utf\-16le|ucs\-4|ucs\-4le|ansi|japan|korea|prc|chinese|taiwan|utf8|unicode|ucs2be|ucs\-2be|ucs\-4be|utf\-32|utf\-32le)\b/,
|
|
7
|
+
vimfuncs: /\b(win_execute|win_findbuf|win_getid|win_gettype|win_gotoid|win_id2tabwin|win_id2win|win_move_separator|win_move_statusline|win_screenpos|win_splitmove|setbufline)\b/,
|
|
8
|
+
builtin_funcs: /\b(highlight|hi|exe|normal!|normal)\b/,
|
|
6
9
|
# Identifiers can include # and special characters
|
|
7
10
|
identifier: /\b[a-zA-Z_][a-zA-Z0-9_#]*\b/,
|
|
8
11
|
# Single-character operators
|
|
@@ -26,7 +29,7 @@ module Vinter
|
|
|
26
29
|
comma: /,/,
|
|
27
30
|
backslash: /\\/,
|
|
28
31
|
question_mark: /\?/,
|
|
29
|
-
command_separator:
|
|
32
|
+
command_separator: /\|/
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
CONTINUATION_OPERATORS = %w(. .. + - * / = == ==# ==? != > < >= <= && || ? : -> =>)
|
|
@@ -41,21 +44,21 @@ module Vinter
|
|
|
41
44
|
def should_parse_as_regex
|
|
42
45
|
# Look at recent tokens to determine if we're in a regex context
|
|
43
46
|
recent_tokens = @tokens.last(3)
|
|
44
|
-
|
|
47
|
+
|
|
45
48
|
# Check for contexts where regex is expected
|
|
46
|
-
return true if recent_tokens.any? { |t|
|
|
47
|
-
t && t[:type] == :keyword && ['syntax'].include?(t[:value])
|
|
49
|
+
return true if recent_tokens.any? { |t|
|
|
50
|
+
t && t[:type] == :keyword && ['syntax'].include?(t[:value])
|
|
48
51
|
}
|
|
49
|
-
|
|
52
|
+
|
|
50
53
|
return true if recent_tokens.any? { |t|
|
|
51
54
|
t && t[:type] == :identifier && ['match', 'region', 'keyword'].include?(t[:value])
|
|
52
55
|
}
|
|
53
|
-
|
|
56
|
+
|
|
54
57
|
# Check for comparison operators that often use regex
|
|
55
58
|
return true if recent_tokens.any? { |t|
|
|
56
59
|
t && t[:type] == :operator && ['=~', '!~', '=~#', '!~#', '=~?', '!~?'].include?(t[:value])
|
|
57
60
|
}
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
false
|
|
60
63
|
end
|
|
61
64
|
|
|
@@ -85,7 +88,7 @@ module Vinter
|
|
|
85
88
|
end
|
|
86
89
|
line_start = temp_pos < 0 || @input[temp_pos] == "\n"
|
|
87
90
|
end
|
|
88
|
-
|
|
91
|
+
|
|
89
92
|
# If we're at the start of a line and it begins with a quote
|
|
90
93
|
if line_start && chunk.start_with?('"')
|
|
91
94
|
# Find the end of the line
|
|
@@ -103,44 +106,125 @@ module Vinter
|
|
|
103
106
|
@column += comment_text.length
|
|
104
107
|
next
|
|
105
108
|
end
|
|
109
|
+
|
|
110
|
+
# --- Interpolated String Handling ---
|
|
111
|
+
if chunk.start_with?("$'")
|
|
112
|
+
i = 2
|
|
113
|
+
string_value = "$'"
|
|
114
|
+
brace_depth = 0
|
|
115
|
+
escaped = false
|
|
116
|
+
|
|
117
|
+
while i < chunk.length
|
|
118
|
+
char = chunk[i]
|
|
119
|
+
string_value += char
|
|
120
|
+
|
|
121
|
+
if char == '\\' && !escaped
|
|
122
|
+
escaped = true
|
|
123
|
+
elsif char == "'" && !escaped && brace_depth == 0
|
|
124
|
+
# End of interpolated string
|
|
125
|
+
i += 1
|
|
126
|
+
break
|
|
127
|
+
elsif char == '{' && !escaped
|
|
128
|
+
brace_depth += 1
|
|
129
|
+
elsif char == '}' && !escaped && brace_depth > 0
|
|
130
|
+
brace_depth -= 1
|
|
131
|
+
elsif escaped
|
|
132
|
+
escaped = false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
i += 1
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@tokens << {
|
|
139
|
+
type: :interpolated_string,
|
|
140
|
+
value: string_value,
|
|
141
|
+
line: @line_num,
|
|
142
|
+
column: @column
|
|
143
|
+
}
|
|
144
|
+
@column += string_value.length
|
|
145
|
+
@position += string_value.length
|
|
146
|
+
@line_num += string_value.count("\n")
|
|
147
|
+
next
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# XXX: i dont like this not being combined with $' condition above
|
|
151
|
+
if chunk.start_with?('$"')
|
|
152
|
+
i = 2
|
|
153
|
+
string_value = '$"'
|
|
154
|
+
brace_depth = 0
|
|
155
|
+
escaped = false
|
|
156
|
+
|
|
157
|
+
while i < chunk.length
|
|
158
|
+
char = chunk[i]
|
|
159
|
+
string_value += char
|
|
160
|
+
|
|
161
|
+
if char == '\\' && !escaped
|
|
162
|
+
escaped = true
|
|
163
|
+
elsif char == '"' && !escaped && brace_depth == 0
|
|
164
|
+
# End of interpolated string
|
|
165
|
+
i += 1
|
|
166
|
+
break
|
|
167
|
+
elsif char == '{' && !escaped
|
|
168
|
+
brace_depth += 1
|
|
169
|
+
elsif char == '}' && !escaped && brace_depth > 0
|
|
170
|
+
brace_depth -= 1
|
|
171
|
+
elsif escaped
|
|
172
|
+
escaped = false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
i += 1
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
@tokens << {
|
|
179
|
+
type: :interpolated_string,
|
|
180
|
+
value: string_value,
|
|
181
|
+
line: @line_num,
|
|
182
|
+
column: @column
|
|
183
|
+
}
|
|
184
|
+
@column += string_value.length
|
|
185
|
+
@position += string_value.length
|
|
186
|
+
@line_num += string_value.count("\n")
|
|
187
|
+
next
|
|
188
|
+
end
|
|
189
|
+
|
|
106
190
|
# Handle string literals manually
|
|
107
191
|
if chunk.start_with?("'") || chunk.start_with?('"')
|
|
108
192
|
quote = chunk[0]
|
|
109
193
|
i = 1
|
|
110
|
-
escaped = false
|
|
111
194
|
string_value = quote
|
|
112
195
|
|
|
113
196
|
# Keep going until we find an unescaped closing quote
|
|
114
197
|
while i < chunk.length
|
|
115
198
|
char = chunk[i]
|
|
199
|
+
next_char = chunk[i + 1] if i + 1 < chunk.length
|
|
200
|
+
|
|
116
201
|
string_value += char
|
|
117
202
|
|
|
118
|
-
if char ==
|
|
119
|
-
escaped
|
|
120
|
-
|
|
121
|
-
|
|
203
|
+
if char == quote && next_char == quote
|
|
204
|
+
# Handle escaped single quote ('') or double quote ("")
|
|
205
|
+
string_value += next_char
|
|
206
|
+
i += 1
|
|
207
|
+
elsif char == quote
|
|
208
|
+
# End of string
|
|
209
|
+
i += 1
|
|
122
210
|
break
|
|
123
|
-
elsif escaped
|
|
124
|
-
escaped = false
|
|
125
211
|
end
|
|
126
212
|
|
|
127
213
|
i += 1
|
|
128
214
|
end
|
|
129
215
|
|
|
130
216
|
# Add the string token if we found a closing quote
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
217
|
+
@tokens << {
|
|
218
|
+
type: :string,
|
|
219
|
+
value: string_value,
|
|
220
|
+
line: @line_num,
|
|
221
|
+
column: @column
|
|
222
|
+
}
|
|
138
223
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
end
|
|
224
|
+
@column += string_value.length
|
|
225
|
+
@position += string_value.length
|
|
226
|
+
@line_num += string_value.count("\n")
|
|
227
|
+
next
|
|
144
228
|
end
|
|
145
229
|
|
|
146
230
|
# Add special handling for command options in the tokenize method
|
|
@@ -186,7 +270,7 @@ module Vinter
|
|
|
186
270
|
end
|
|
187
271
|
|
|
188
272
|
# Check for keywords first, before other token types
|
|
189
|
-
if match = chunk.match(/\A\b(if|else|elseif|endif|while|endwhile|for|endfor|def|enddef|function|endfunction|endfunc|return|const|var|final|import|export|class|extends|static|enum|type|vim9script|abort|autocmd|echoerr|echohl|echomsg|let|unlet|execute|setlocal|syntax|highlight|sleep|source)\b/)
|
|
273
|
+
if match = chunk.match(/\A\b(if|else|elseif|endif|while|endwhile|for|endfor|def|enddef|function|endfunction|endfunc|return|const|var|final|import|export|class|extends|static|enum|type|vim9script|abort|autocmd|echoerr|echohl|echomsg|let|unlet|var|execute|setlocal|syntax|highlight|sleep|source)\b/)
|
|
190
274
|
@tokens << {
|
|
191
275
|
type: :keyword,
|
|
192
276
|
value: match[0],
|
|
@@ -210,7 +294,7 @@ module Vinter
|
|
|
210
294
|
@position += match[0].length
|
|
211
295
|
next
|
|
212
296
|
end
|
|
213
|
-
|
|
297
|
+
|
|
214
298
|
# Handle Vim option variables with & prefix
|
|
215
299
|
if match = chunk.match(/\A&[a-zA-Z_][a-zA-Z0-9_]*/)
|
|
216
300
|
@tokens << {
|
|
@@ -330,7 +414,7 @@ module Vinter
|
|
|
330
414
|
end
|
|
331
415
|
|
|
332
416
|
# Add support for standalone namespace prefixes (like g:)
|
|
333
|
-
if match = chunk.match(/\A([
|
|
417
|
+
if match = chunk.match(/\A([gbwt]):/)
|
|
334
418
|
@tokens << {
|
|
335
419
|
type: :namespace_prefix,
|
|
336
420
|
value: match[0],
|
|
@@ -385,21 +469,21 @@ module Vinter
|
|
|
385
469
|
if chunk.start_with?('/') && should_parse_as_regex
|
|
386
470
|
i = 1
|
|
387
471
|
regex_value = '/'
|
|
388
|
-
|
|
472
|
+
|
|
389
473
|
# Keep going until we find the closing slash
|
|
390
474
|
while i < chunk.length
|
|
391
475
|
char = chunk[i]
|
|
392
476
|
regex_value += char
|
|
393
|
-
|
|
477
|
+
|
|
394
478
|
if char == '/' && (i == 1 || chunk[i-1] != '\\')
|
|
395
479
|
# Found closing slash
|
|
396
480
|
i += 1
|
|
397
481
|
break
|
|
398
482
|
end
|
|
399
|
-
|
|
483
|
+
|
|
400
484
|
i += 1
|
|
401
485
|
end
|
|
402
|
-
|
|
486
|
+
|
|
403
487
|
# Add the regex token if we found a closing slash
|
|
404
488
|
if regex_value.end_with?('/')
|
|
405
489
|
@tokens << {
|
|
@@ -442,8 +526,7 @@ module Vinter
|
|
|
442
526
|
end
|
|
443
527
|
|
|
444
528
|
# In the tokenize method, add special handling for common mapping components
|
|
445
|
-
if chunk.start_with?('<CR>', '<Esc>', '<Tab>', '<Space>', '<C-')
|
|
446
|
-
(chunk =~ /\A<[A-Za-z0-9\-_]+>/)
|
|
529
|
+
if chunk.start_with?('<CR>', '<Esc>', '<Tab>', '<Space>', '<C-')
|
|
447
530
|
# Extract the special key notation
|
|
448
531
|
match = chunk.match(/\A(<[^>]+>)/)
|
|
449
532
|
if match
|
|
@@ -486,13 +569,24 @@ module Vinter
|
|
|
486
569
|
@column += 1
|
|
487
570
|
@position += 1
|
|
488
571
|
|
|
489
|
-
# If followed by a newline, advance to next line
|
|
572
|
+
# If followed by a newline, advance to the next line
|
|
490
573
|
if @position < @input.length && @input[@position] == "\n"
|
|
491
574
|
@line_num += 1
|
|
492
575
|
@column = 1
|
|
493
576
|
@position += 1
|
|
494
577
|
end
|
|
495
578
|
|
|
579
|
+
# Skip whitespace after the continuation
|
|
580
|
+
while @position < @input.length && @input[@position] =~ /\s/
|
|
581
|
+
if @input[@position] == "\n"
|
|
582
|
+
@line_num += 1
|
|
583
|
+
@column = 1
|
|
584
|
+
else
|
|
585
|
+
@column += 1
|
|
586
|
+
end
|
|
587
|
+
@position += 1
|
|
588
|
+
end
|
|
589
|
+
|
|
496
590
|
next
|
|
497
591
|
end
|
|
498
592
|
|