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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 319f22f198207f7d9d3b9e58c85327b20f7c873e905bc16a38acd6f9199d8b56
4
- data.tar.gz: c5eeb2b1dd21094be8a901e570540b89a29619273b0c5fcb11b7edc278da9975
3
+ metadata.gz: 617e3da0fed64bcf718e3c1e3bd82a66e910e4a2099a27942a2390c93421779b
4
+ data.tar.gz: a54306cbf4b2e25921c3518985acb0d648096a66e1f24a86e59ac3aa53d56810
5
5
  SHA512:
6
- metadata.gz: b1556c23fd42b7b7d9d3163a97ecd353902f12341c203a913a4dffec05cbd6ffe867815f223dbfe8f169fd99c514b8b39ac1fab87fd26f195d0622fff9e62966
7
- data.tar.gz: 2204f688018212f0b5ec6b13a0906da8a24078fe874dc10c2b9b326668627fb985fdcc11b9682bdbb67f2a452838b8088286260de54bf8941b6501180a7a8704
6
+ metadata.gz: 614e2e7ef3f02f16a8888069c40244761a7c3afb142b84aafb410c39693e25d4e3d9afa9e4a8bafea24512dc9b339364bf8e0ace1a665d424d0f638428d4b7f9
7
+ data.tar.gz: 8ace7e570a66a113e66c411d5725f1a72fc3596faffcda899e3590b37b7893aaff21aeefc4d1e1e60fa2cc20f830377417c493b88af4ab90f126f72b843ee521
data/lib/vinter/cli.rb CHANGED
@@ -6,7 +6,7 @@ module Vinter
6
6
 
7
7
  def run(args)
8
8
  if args.empty?
9
- puts "Usage: vinter [file.vim|directory] [--print-ast]"
9
+ puts "Usage: vinter [file.vim|directory]"
10
10
  return 1
11
11
  end
12
12
 
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|highlight|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|default)\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)\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 == '\\' && !escaped
162
- escaped = true
163
- elsif (char == "\n" or char == quote) && !escaped
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
- if i < chunk.length || (i == chunk.length && chunk[-1] == quote)
175
- @tokens << {
176
- type: :string,
177
- value: string_value,
178
- line: @line_num,
179
- column: @column
180
- }
217
+ @tokens << {
218
+ type: :string,
219
+ value: string_value,
220
+ line: @line_num,
221
+ column: @column
222
+ }
181
223
 
182
- @column += string_value.length
183
- @position += string_value.length
184
- @line_num += 1 if string_value.include?("\n")
185
- next
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([sgbwtal]):/)
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
- name = current_token[:value]
429
- #advance#skip value
430
- #advance#operator
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(token[:value], line, column)
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(token[:value], line, column)
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(token[:value], line, column)
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(name, line, column)
2056
- # Skip the function name (already consumed)
2057
- advance if current_token[:value] == name
2058
-
2059
- # Check if there's an opening parenthesis
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
- # Check for closing parenthesis
2108
- if current_token && current_token[:type] == :paren_close
2109
- advance # Skip ')'
2110
- else
2111
- @errors << {
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 comments inside parameter lists
2690
- if current_token && current_token[:type] == :comment
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
- # Break if we hit the closing paren
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 && current_token[:type] != :comment
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
@@ -5,5 +5,5 @@ require "vinter/cli"
5
5
  require "vinter/ast_printer"
6
6
 
7
7
  module Vinter
8
- VERSION = "0.5.0"
8
+ VERSION = "0.6.0"
9
9
  end
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.5.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: 2025-11-26 00:00:00.000000000 Z
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.5.11
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: []