vinter 0.5.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/cli.rb +1 -1
- data/lib/vinter/lexer.rb +76 -25
- data/lib/vinter/parser.rb +234 -120
- data/lib/vinter.rb +1 -1
- metadata +3 -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
|
data/lib/vinter/cli.rb
CHANGED
data/lib/vinter/lexer.rb
CHANGED
|
@@ -2,9 +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|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|
|
|
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
|
|
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)\b/,
|
|
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/,
|
|
8
9
|
# Identifiers can include # and special characters
|
|
9
10
|
identifier: /\b[a-zA-Z_][a-zA-Z0-9_#]*\b/,
|
|
10
11
|
# Single-character operators
|
|
@@ -146,44 +147,84 @@ module Vinter
|
|
|
146
147
|
next
|
|
147
148
|
end
|
|
148
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
|
+
|
|
149
190
|
# Handle string literals manually
|
|
150
191
|
if chunk.start_with?("'") || chunk.start_with?('"')
|
|
151
192
|
quote = chunk[0]
|
|
152
193
|
i = 1
|
|
153
|
-
escaped = false
|
|
154
194
|
string_value = quote
|
|
155
195
|
|
|
156
196
|
# Keep going until we find an unescaped closing quote
|
|
157
197
|
while i < chunk.length
|
|
158
198
|
char = chunk[i]
|
|
199
|
+
next_char = chunk[i + 1] if i + 1 < chunk.length
|
|
200
|
+
|
|
159
201
|
string_value += char
|
|
160
202
|
|
|
161
|
-
if char ==
|
|
162
|
-
escaped
|
|
163
|
-
|
|
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
|
|
164
209
|
i += 1
|
|
165
210
|
break
|
|
166
|
-
elsif escaped
|
|
167
|
-
escaped = false
|
|
168
211
|
end
|
|
169
212
|
|
|
170
213
|
i += 1
|
|
171
214
|
end
|
|
172
215
|
|
|
173
216
|
# Add the string token if we found a closing quote
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
217
|
+
@tokens << {
|
|
218
|
+
type: :string,
|
|
219
|
+
value: string_value,
|
|
220
|
+
line: @line_num,
|
|
221
|
+
column: @column
|
|
222
|
+
}
|
|
181
223
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
end
|
|
224
|
+
@column += string_value.length
|
|
225
|
+
@position += string_value.length
|
|
226
|
+
@line_num += string_value.count("\n")
|
|
227
|
+
next
|
|
187
228
|
end
|
|
188
229
|
|
|
189
230
|
# Add special handling for command options in the tokenize method
|
|
@@ -373,7 +414,7 @@ module Vinter
|
|
|
373
414
|
end
|
|
374
415
|
|
|
375
416
|
# Add support for standalone namespace prefixes (like g:)
|
|
376
|
-
if match = chunk.match(/\A([
|
|
417
|
+
if match = chunk.match(/\A([gbwt]):/)
|
|
377
418
|
@tokens << {
|
|
378
419
|
type: :namespace_prefix,
|
|
379
420
|
value: match[0],
|
|
@@ -485,8 +526,7 @@ module Vinter
|
|
|
485
526
|
end
|
|
486
527
|
|
|
487
528
|
# In the tokenize method, add special handling for common mapping components
|
|
488
|
-
if chunk.start_with?('<CR>', '<Esc>', '<Tab>', '<Space>', '<C-')
|
|
489
|
-
(chunk =~ /\A<[A-Za-z0-9\-_]+>/)
|
|
529
|
+
if chunk.start_with?('<CR>', '<Esc>', '<Tab>', '<Space>', '<C-')
|
|
490
530
|
# Extract the special key notation
|
|
491
531
|
match = chunk.match(/\A(<[^>]+>)/)
|
|
492
532
|
if match
|
|
@@ -529,13 +569,24 @@ module Vinter
|
|
|
529
569
|
@column += 1
|
|
530
570
|
@position += 1
|
|
531
571
|
|
|
532
|
-
# If followed by a newline, advance to next line
|
|
572
|
+
# If followed by a newline, advance to the next line
|
|
533
573
|
if @position < @input.length && @input[@position] == "\n"
|
|
534
574
|
@line_num += 1
|
|
535
575
|
@column = 1
|
|
536
576
|
@position += 1
|
|
537
577
|
end
|
|
538
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
|
+
|
|
539
590
|
next
|
|
540
591
|
end
|
|
541
592
|
|
data/lib/vinter/parser.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'pry'
|
|
1
2
|
module Vinter
|
|
2
3
|
class Parser
|
|
3
4
|
def initialize(tokens, source_text = nil)
|
|
@@ -244,7 +245,6 @@ module Vinter
|
|
|
244
245
|
if !current_token
|
|
245
246
|
return nil
|
|
246
247
|
end
|
|
247
|
-
start_token = current_token
|
|
248
248
|
|
|
249
249
|
# Handle pipe as command separator
|
|
250
250
|
if current_token[:type] == :operator && current_token[:value] == '|'
|
|
@@ -378,7 +378,7 @@ module Vinter
|
|
|
378
378
|
parse_set_command
|
|
379
379
|
when 'syntax'
|
|
380
380
|
parse_syntax_command
|
|
381
|
-
when 'highlight'
|
|
381
|
+
when 'highlight', 'hi'
|
|
382
382
|
parse_highlight_command
|
|
383
383
|
when 'sleep'
|
|
384
384
|
parse_sleep_command
|
|
@@ -425,23 +425,23 @@ module Vinter
|
|
|
425
425
|
elsif current_token[:type] == :percentage
|
|
426
426
|
parse_range_command
|
|
427
427
|
elsif current_token[:type] == :global_variable
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
##parse_expression_statement
|
|
432
|
-
#advance#value
|
|
433
|
-
while peek_token[:line] == current_token[:line]
|
|
434
|
-
advance
|
|
435
|
-
end
|
|
436
|
-
advance
|
|
437
|
-
{
|
|
438
|
-
type: :global_variable,
|
|
439
|
-
name: name
|
|
440
|
-
}
|
|
441
|
-
|
|
428
|
+
parse_global_variable
|
|
429
|
+
elsif current_token[:type] == :buffer_local
|
|
430
|
+
parse_buffer_var
|
|
442
431
|
elsif current_token[:type] == :vimfuncs
|
|
443
432
|
advance
|
|
444
433
|
parse_function_call(current_token[:value], current_token[:line], current_token[:column])
|
|
434
|
+
elsif current_token[:type] == :builtin_funcs
|
|
435
|
+
parse_builtin_function_call
|
|
436
|
+
elsif current_token[:type] == :compound_operator
|
|
437
|
+
advance
|
|
438
|
+
parse_expression
|
|
439
|
+
elsif current_token[:type] == :string
|
|
440
|
+
parse_string
|
|
441
|
+
elsif current_token[:type] == :comma
|
|
442
|
+
advance
|
|
443
|
+
elsif current_token[:type] == :colon
|
|
444
|
+
parse_command_line_call
|
|
445
445
|
else
|
|
446
446
|
@warnings << {
|
|
447
447
|
message: "Unexpected token type: #{current_token[:type]}",
|
|
@@ -454,6 +454,182 @@ module Vinter
|
|
|
454
454
|
end
|
|
455
455
|
end
|
|
456
456
|
|
|
457
|
+
def parse_command_line_call
|
|
458
|
+
token = advance # Skip the ':'
|
|
459
|
+
line = token[:line]
|
|
460
|
+
column = token[:column]
|
|
461
|
+
|
|
462
|
+
# Extract the command or range
|
|
463
|
+
command = token[:value][1..] # Remove the leading ':'
|
|
464
|
+
|
|
465
|
+
# Check if the command is a range (e.g., `:2`, `:$`)
|
|
466
|
+
if command.match?(/^\d+$/) || command == '$'
|
|
467
|
+
return {
|
|
468
|
+
type: :range_command,
|
|
469
|
+
range: command,
|
|
470
|
+
line: line,
|
|
471
|
+
column: column
|
|
472
|
+
}
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Otherwise, it's a regular command (e.g., `:exe`)
|
|
476
|
+
args = []
|
|
477
|
+
while current_token && current_token[:type] != :newline
|
|
478
|
+
args << current_token[:value]
|
|
479
|
+
advance
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
{
|
|
483
|
+
type: :command_line_call,
|
|
484
|
+
command: command,
|
|
485
|
+
arguments: args,
|
|
486
|
+
line: line,
|
|
487
|
+
column: column
|
|
488
|
+
}
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def parse_buffer_var
|
|
492
|
+
token = current_token
|
|
493
|
+
line = token[:line]
|
|
494
|
+
column = token[:column]
|
|
495
|
+
name = token[:value]
|
|
496
|
+
advance
|
|
497
|
+
|
|
498
|
+
# Handle property access (e.g., b:foo.bar)
|
|
499
|
+
target = {
|
|
500
|
+
type: :buffer_variable,
|
|
501
|
+
name: name,
|
|
502
|
+
line: line,
|
|
503
|
+
column: column
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# Property access (dot notation)
|
|
507
|
+
# shared with global (TODO)
|
|
508
|
+
# would almost just take an overly simple check to the = operator and call everything after b: name
|
|
509
|
+
if current_token && current_token[:type] == :operator && current_token[:value] == '.'
|
|
510
|
+
dot_token = advance
|
|
511
|
+
if current_token && (current_token[:type] == :identifier || current_token[:type] == :keyword)
|
|
512
|
+
property_token = advance
|
|
513
|
+
target = {
|
|
514
|
+
type: :property_access,
|
|
515
|
+
object: target,
|
|
516
|
+
property: property_token[:value],
|
|
517
|
+
line: dot_token[:line],
|
|
518
|
+
column: dot_token[:column]
|
|
519
|
+
}
|
|
520
|
+
else
|
|
521
|
+
add_error("Expected property name after '.' in variable")
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Indexed access (e.g., b:foo[bar])
|
|
526
|
+
while current_token && current_token[:type] == :bracket_open
|
|
527
|
+
bracket_token = advance
|
|
528
|
+
index_expr = parse_expression
|
|
529
|
+
expect(:bracket_close)
|
|
530
|
+
target = {
|
|
531
|
+
type: :indexed_access,
|
|
532
|
+
object: target,
|
|
533
|
+
index: index_expr,
|
|
534
|
+
line: bracket_token[:line],
|
|
535
|
+
column: bracket_token[:column]
|
|
536
|
+
}
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
# Assignment operator (= or compound)
|
|
540
|
+
operator = nil
|
|
541
|
+
if current_token && (
|
|
542
|
+
(current_token[:type] == :operator && current_token[:value] == '=') ||
|
|
543
|
+
current_token[:type] == :compound_operator)
|
|
544
|
+
operator = current_token[:value]
|
|
545
|
+
advance
|
|
546
|
+
else
|
|
547
|
+
add_error("Expected assignment operator after global variable")
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# Value expression
|
|
551
|
+
value = parse_expression
|
|
552
|
+
|
|
553
|
+
{
|
|
554
|
+
type: :buffer_variable_assignment,
|
|
555
|
+
target: target,
|
|
556
|
+
operator: operator,
|
|
557
|
+
value: value,
|
|
558
|
+
line: line,
|
|
559
|
+
column: column
|
|
560
|
+
}
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def parse_global_variable
|
|
564
|
+
token = current_token
|
|
565
|
+
line = token[:line]
|
|
566
|
+
column = token[:column]
|
|
567
|
+
name = token[:value]
|
|
568
|
+
advance # Skip the global variable token
|
|
569
|
+
|
|
570
|
+
# Handle property access (e.g., g:foo.bar)
|
|
571
|
+
target = {
|
|
572
|
+
type: :global_variable,
|
|
573
|
+
name: name,
|
|
574
|
+
line: line,
|
|
575
|
+
column: column
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
# Property access (dot notation)
|
|
579
|
+
if current_token && current_token[:type] == :operator && current_token[:value] == '.'
|
|
580
|
+
dot_token = advance
|
|
581
|
+
if current_token && (current_token[:type] == :identifier || current_token[:type] == :keyword)
|
|
582
|
+
property_token = advance
|
|
583
|
+
target = {
|
|
584
|
+
type: :property_access,
|
|
585
|
+
object: target,
|
|
586
|
+
property: property_token[:value],
|
|
587
|
+
line: dot_token[:line],
|
|
588
|
+
column: dot_token[:column]
|
|
589
|
+
}
|
|
590
|
+
else
|
|
591
|
+
add_error("Expected property name after '.' in global variable")
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Indexed access (e.g., g:foo[bar])
|
|
596
|
+
while current_token && current_token[:type] == :bracket_open
|
|
597
|
+
bracket_token = advance
|
|
598
|
+
index_expr = parse_expression
|
|
599
|
+
expect(:bracket_close)
|
|
600
|
+
target = {
|
|
601
|
+
type: :indexed_access,
|
|
602
|
+
object: target,
|
|
603
|
+
index: index_expr,
|
|
604
|
+
line: bracket_token[:line],
|
|
605
|
+
column: bracket_token[:column]
|
|
606
|
+
}
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
# Assignment operator (= or compound)
|
|
610
|
+
operator = nil
|
|
611
|
+
if current_token && (
|
|
612
|
+
(current_token[:type] == :operator && current_token[:value] == '=') ||
|
|
613
|
+
current_token[:type] == :compound_operator)
|
|
614
|
+
operator = current_token[:value]
|
|
615
|
+
advance
|
|
616
|
+
else
|
|
617
|
+
add_error("Expected assignment operator after global variable")
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Value expression
|
|
621
|
+
value = parse_expression
|
|
622
|
+
|
|
623
|
+
{
|
|
624
|
+
type: :global_variable_assignment,
|
|
625
|
+
target: target,
|
|
626
|
+
operator: operator,
|
|
627
|
+
value: value,
|
|
628
|
+
line: line,
|
|
629
|
+
column: column
|
|
630
|
+
}
|
|
631
|
+
end
|
|
632
|
+
|
|
457
633
|
def parse_range_command
|
|
458
634
|
token = advance # Skip '%'
|
|
459
635
|
line = token[:line]
|
|
@@ -847,9 +1023,11 @@ module Vinter
|
|
|
847
1023
|
event = nil
|
|
848
1024
|
if current_token && current_token[:type] == :identifier
|
|
849
1025
|
event = advance[:value]
|
|
1026
|
+
elsif current_token[:type] == :operator && current_token[:value] == "!"
|
|
1027
|
+
advance
|
|
850
1028
|
else
|
|
851
1029
|
@errors << {
|
|
852
|
-
message: "Expected event name after 'autocmd'",
|
|
1030
|
+
message: "Expected event name after 'autocmd' | #{current_token[:type]}",
|
|
853
1031
|
position: @position,
|
|
854
1032
|
line: current_token ? current_token[:line] : 0,
|
|
855
1033
|
column: current_token ? current_token[:column] : 0
|
|
@@ -1117,7 +1295,7 @@ module Vinter
|
|
|
1117
1295
|
|
|
1118
1296
|
# Parse optional return type
|
|
1119
1297
|
return_type = nil
|
|
1120
|
-
if current_token && current_token[:type] == :colon
|
|
1298
|
+
if current_token && current_token[:type] == :colon && current_token[:line] == line
|
|
1121
1299
|
advance # Skip ':'
|
|
1122
1300
|
return_type = parse_type
|
|
1123
1301
|
end
|
|
@@ -1175,12 +1353,6 @@ module Vinter
|
|
|
1175
1353
|
break
|
|
1176
1354
|
end
|
|
1177
1355
|
|
|
1178
|
-
# Get parameter name
|
|
1179
|
-
if !current_token || current_token[:type] != :identifier
|
|
1180
|
-
add_error("Expected parameter name")
|
|
1181
|
-
break
|
|
1182
|
-
end
|
|
1183
|
-
|
|
1184
1356
|
param_name = advance
|
|
1185
1357
|
|
|
1186
1358
|
# Check for type annotation
|
|
@@ -1270,7 +1442,7 @@ module Vinter
|
|
|
1270
1442
|
|
|
1271
1443
|
# Parse initializer if present
|
|
1272
1444
|
initializer = nil
|
|
1273
|
-
if current_token && current_token[:type] == :operator && current_token[:value] == '='
|
|
1445
|
+
if current_token && (current_token[:type] == :operator && current_token[:value] == '=')
|
|
1274
1446
|
advance # Skip '='
|
|
1275
1447
|
initializer = parse_expression
|
|
1276
1448
|
end
|
|
@@ -1563,7 +1735,7 @@ module Vinter
|
|
|
1563
1735
|
# Special handling for map-related keywords when they appear in expressions
|
|
1564
1736
|
if ['map', 'nmap', 'imap', 'vmap', 'xmap', 'noremap', 'nnoremap', 'inoremap', 'vnoremap', 'xnoremap', 'cnoremap', 'cmap'].include?(token[:value])
|
|
1565
1737
|
if peek_token[:type] == :paren_open
|
|
1566
|
-
parse_builtin_function_call
|
|
1738
|
+
parse_builtin_function_call
|
|
1567
1739
|
else
|
|
1568
1740
|
# Treat map commands as identifiers when inside expressions
|
|
1569
1741
|
advance
|
|
@@ -1576,7 +1748,7 @@ module Vinter
|
|
|
1576
1748
|
end
|
|
1577
1749
|
elsif token[:value] == 'type' && current_token && (current_token[:type] == :paren_open || peek_token && peek_token[:type] == :paren_open)
|
|
1578
1750
|
# This is the type() function call
|
|
1579
|
-
return parse_builtin_function_call
|
|
1751
|
+
return parse_builtin_function_call
|
|
1580
1752
|
elsif token[:value] == 'function'
|
|
1581
1753
|
# Check if this is a function() call or just 'function' as property name
|
|
1582
1754
|
if peek_token && peek_token[:type] == :paren_open
|
|
@@ -1662,6 +1834,8 @@ module Vinter
|
|
|
1662
1834
|
line: line,
|
|
1663
1835
|
column: column
|
|
1664
1836
|
}
|
|
1837
|
+
elsif token[:value] == 'command'
|
|
1838
|
+
advance
|
|
1665
1839
|
else
|
|
1666
1840
|
@errors << {
|
|
1667
1841
|
message: "Unexpected keyword in expression: #{token[:value]}",
|
|
@@ -1789,7 +1963,7 @@ module Vinter
|
|
|
1789
1963
|
when :identifier
|
|
1790
1964
|
# Special handling for Vim built-in functions
|
|
1791
1965
|
if ['has', 'exists', 'empty', 'get', 'type', 'map', 'copy'].include?(token[:value])
|
|
1792
|
-
expr = parse_builtin_function_call
|
|
1966
|
+
expr = parse_builtin_function_call
|
|
1793
1967
|
else
|
|
1794
1968
|
advance
|
|
1795
1969
|
|
|
@@ -1865,7 +2039,8 @@ module Vinter
|
|
|
1865
2039
|
advance
|
|
1866
2040
|
expr = parse_expression
|
|
1867
2041
|
when :interpolated_string
|
|
1868
|
-
advance
|
|
2042
|
+
token = advance
|
|
2043
|
+
value = token[:value]
|
|
1869
2044
|
expr = {
|
|
1870
2045
|
type: :interpolated_string,
|
|
1871
2046
|
value: token[:value],
|
|
@@ -1926,6 +2101,7 @@ module Vinter
|
|
|
1926
2101
|
# Check for method call with arrow ->
|
|
1927
2102
|
elsif current_token[:type] == :operator && current_token[:value] == '->'
|
|
1928
2103
|
arrow_token = advance # Skip '->'
|
|
2104
|
+
advance if current_token[:type] == :line_continuation
|
|
1929
2105
|
|
|
1930
2106
|
# Next token should be an identifier (method name)
|
|
1931
2107
|
if !current_token || current_token[:type] != :identifier
|
|
@@ -1990,6 +2166,12 @@ module Vinter
|
|
|
1990
2166
|
else
|
|
1991
2167
|
end_index = parse_expression
|
|
1992
2168
|
end
|
|
2169
|
+
# handles array assignment [a,b] = split(thing)
|
|
2170
|
+
elsif current_token[:type] == :comma
|
|
2171
|
+
while current_token[:type] == :comma
|
|
2172
|
+
advance
|
|
2173
|
+
parse_expression
|
|
2174
|
+
end
|
|
1993
2175
|
end
|
|
1994
2176
|
|
|
1995
2177
|
expect(:bracket_close) # Skip ']'
|
|
@@ -2052,76 +2234,19 @@ module Vinter
|
|
|
2052
2234
|
return expr
|
|
2053
2235
|
end
|
|
2054
2236
|
|
|
2055
|
-
def parse_builtin_function_call
|
|
2056
|
-
#
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
if current_token && current_token[:type] == :paren_open
|
|
2061
|
-
advance # Skip '('
|
|
2062
|
-
|
|
2063
|
-
# Parse arguments
|
|
2064
|
-
args = []
|
|
2065
|
-
|
|
2066
|
-
# Functions that take string expressions as code
|
|
2067
|
-
special_functions = ['map', 'reduce', 'sort', 'call', 'eval', 'execute', 'exec']
|
|
2068
|
-
is_special_function = special_functions.include?(name)
|
|
2069
|
-
|
|
2070
|
-
# Parse until closing parenthesis
|
|
2071
|
-
while @position < @tokens.length && current_token && current_token[:type] != :paren_close
|
|
2072
|
-
# Skip whitespace or comments
|
|
2073
|
-
if current_token[:type] == :whitespace || current_token[:type] == :comment
|
|
2074
|
-
advance
|
|
2075
|
-
next
|
|
2076
|
-
end
|
|
2077
|
-
|
|
2078
|
-
# Special handling for string arguments that contain code
|
|
2079
|
-
if is_special_function &&
|
|
2080
|
-
current_token && current_token[:type] == :string
|
|
2081
|
-
string_token = parse_expression
|
|
2082
|
-
args << {
|
|
2083
|
-
type: :literal,
|
|
2084
|
-
value: string_token[:value],
|
|
2085
|
-
token_type: :string,
|
|
2086
|
-
line: string_token[:line],
|
|
2087
|
-
column: string_token[:column]
|
|
2088
|
-
}
|
|
2089
|
-
else
|
|
2090
|
-
arg = parse_expression
|
|
2091
|
-
args << arg if arg
|
|
2092
|
-
end
|
|
2093
|
-
|
|
2094
|
-
if current_token && current_token[:type] == :comma
|
|
2095
|
-
advance
|
|
2096
|
-
elsif current_token && current_token[:type] != :paren_close
|
|
2097
|
-
@errors << {
|
|
2098
|
-
message: "Expected comma or closing parenthesis in #{name} function",
|
|
2099
|
-
position: @position,
|
|
2100
|
-
line: current_token[:line],
|
|
2101
|
-
column: current_token[:column]
|
|
2102
|
-
}
|
|
2103
|
-
break
|
|
2104
|
-
end
|
|
2105
|
-
end
|
|
2237
|
+
def parse_builtin_function_call
|
|
2238
|
+
token = advance # Consume the `builtin_funcs` token
|
|
2239
|
+
line = token[:line]
|
|
2240
|
+
column = token[:column]
|
|
2241
|
+
name = token[:value]
|
|
2106
2242
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
message: "Expected ')' to close #{name} function call",
|
|
2113
|
-
position: @position,
|
|
2114
|
-
line: current_token ? current_token[:line] : 0,
|
|
2115
|
-
column: current_token ? current_token[:column] : 0
|
|
2116
|
-
}
|
|
2117
|
-
end
|
|
2118
|
-
else
|
|
2119
|
-
# Handle legacy Vim script where parentheses might be omitted
|
|
2120
|
-
# Just parse one expression as the argument
|
|
2121
|
-
args = [parse_expression]
|
|
2243
|
+
# Collect arguments (if any) until the end of the line
|
|
2244
|
+
args = []
|
|
2245
|
+
while current_token && current_token[:type] != :comment && current_token[:value] != "\n"
|
|
2246
|
+
args << current_token[:value]
|
|
2247
|
+
advance
|
|
2122
2248
|
end
|
|
2123
2249
|
|
|
2124
|
-
# Return function call node
|
|
2125
2250
|
{
|
|
2126
2251
|
type: :builtin_function_call,
|
|
2127
2252
|
name: name,
|
|
@@ -2680,42 +2805,30 @@ module Vinter
|
|
|
2680
2805
|
|
|
2681
2806
|
args = []
|
|
2682
2807
|
|
|
2683
|
-
# Special handling for Vim functions that take code strings as arguments
|
|
2684
|
-
special_functions = ['map', 'reduce', 'sort', 'call', 'eval', 'execute', 'exec']
|
|
2685
|
-
is_special_function = special_functions.include?(name)
|
|
2686
|
-
|
|
2687
2808
|
# Parse arguments until we find a closing parenthesis
|
|
2688
2809
|
while @position < @tokens.length && current_token && current_token[:type] != :paren_close
|
|
2689
|
-
# Skip
|
|
2690
|
-
if current_token
|
|
2810
|
+
# Skip line continuations
|
|
2811
|
+
if current_token[:type] == :line_continuation
|
|
2691
2812
|
advance
|
|
2692
2813
|
next
|
|
2693
2814
|
end
|
|
2694
|
-
if is_special_function && current_token && current_token[:type] == :string
|
|
2695
|
-
# For functions like map(), filter(), directly add the string as an argument
|
|
2696
|
-
string_token = parse_string
|
|
2697
|
-
args << {
|
|
2698
|
-
type: :literal,
|
|
2699
|
-
value: string_token[:value],
|
|
2700
|
-
token_type: :string,
|
|
2701
|
-
line: string_token[:line],
|
|
2702
|
-
column: string_token[:column]
|
|
2703
|
-
}
|
|
2704
|
-
else
|
|
2705
|
-
# Parse the argument
|
|
2706
|
-
arg = parse_expression
|
|
2707
|
-
args << arg if arg
|
|
2708
|
-
end
|
|
2709
2815
|
|
|
2710
|
-
#
|
|
2816
|
+
# Parse the argument
|
|
2817
|
+
arg = parse_expression
|
|
2818
|
+
args << arg if arg
|
|
2819
|
+
|
|
2820
|
+
# Break if we hit the closing parenthesis
|
|
2711
2821
|
if current_token && current_token[:type] == :paren_close
|
|
2712
2822
|
break
|
|
2713
2823
|
end
|
|
2824
|
+
|
|
2714
2825
|
# If we have a comma, advance past it and continue
|
|
2715
2826
|
if current_token && current_token[:type] == :comma
|
|
2716
2827
|
advance
|
|
2828
|
+
elsif current_token[:type] == :line_continuation
|
|
2829
|
+
advance
|
|
2717
2830
|
# If we don't have a comma and we're not at the end, it's an error
|
|
2718
|
-
elsif current_token && current_token[:type] != :paren_close
|
|
2831
|
+
elsif current_token && current_token[:type] != :paren_close
|
|
2719
2832
|
@errors << {
|
|
2720
2833
|
message: "Expected comma or closing parenthesis after argument",
|
|
2721
2834
|
position: @position,
|
|
@@ -2966,7 +3079,7 @@ module Vinter
|
|
|
2966
3079
|
advance # Skip the '='
|
|
2967
3080
|
|
|
2968
3081
|
# Get the value (number or identifier)
|
|
2969
|
-
if current_token && (current_token[:type] == :number || current_token[:type] == :identifier)
|
|
3082
|
+
if current_token && (current_token[:type] == :number || current_token[:type] == :identifier) || current_token[:type] == :operator
|
|
2970
3083
|
attribute += "=#{current_token[:value]}"
|
|
2971
3084
|
attributes << attribute
|
|
2972
3085
|
advance
|
|
@@ -2976,6 +3089,7 @@ module Vinter
|
|
|
2976
3089
|
|
|
2977
3090
|
# Parse the command name
|
|
2978
3091
|
command_name = nil
|
|
3092
|
+
advance if current_token[:type] == :line_continuation
|
|
2979
3093
|
if current_token && current_token[:type] == :identifier
|
|
2980
3094
|
command_name = current_token[:value]
|
|
2981
3095
|
advance
|
data/lib/vinter.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vinter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dan Bradbury
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: A linter for the Vim9 script language, helping to identify issues and
|
|
14
13
|
enforce best practices
|
|
@@ -31,7 +30,6 @@ homepage: https://github.com/DanBradbury/vinter
|
|
|
31
30
|
licenses:
|
|
32
31
|
- MIT
|
|
33
32
|
metadata: {}
|
|
34
|
-
post_install_message:
|
|
35
33
|
rdoc_options: []
|
|
36
34
|
require_paths:
|
|
37
35
|
- lib
|
|
@@ -46,8 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
46
44
|
- !ruby/object:Gem::Version
|
|
47
45
|
version: '0'
|
|
48
46
|
requirements: []
|
|
49
|
-
rubygems_version: 3.
|
|
50
|
-
signing_key:
|
|
47
|
+
rubygems_version: 3.6.9
|
|
51
48
|
specification_version: 4
|
|
52
49
|
summary: A linter for leagacy + Vim9 script
|
|
53
50
|
test_files: []
|