vinter 0.5.0 → 0.6.1

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: 18b99e927ea645baaa6bf8290f53625a800c64a36e7bcb681d8096f7591ab862
4
+ data.tar.gz: 35660a038fcc98c284731d75fe3644d41ff5a9212dd1ca7930d21d72b9f02764
5
5
  SHA512:
6
- metadata.gz: b1556c23fd42b7b7d9d3163a97ecd353902f12341c203a913a4dffec05cbd6ffe867815f223dbfe8f169fd99c514b8b39ac1fab87fd26f195d0622fff9e62966
7
- data.tar.gz: 2204f688018212f0b5ec6b13a0906da8a24078fe874dc10c2b9b326668627fb985fdcc11b9682bdbb67f2a452838b8088286260de54bf8941b6501180a7a8704
6
+ metadata.gz: f9ed5e432fcdc6e0a88545e15da0996dda4c31176c88fdcbb46f2ea4228bc4c431fe1e569b3e7420f370ab8147ac290738ac27dd21f1f7bed0a0d2093da5b933
7
+ data.tar.gz: be685a411f5592425d122ea8a3080208c1cad58566dc88b7e5850a0e017ae9beebfff1c1a019955a47eb7e54a732ba94e4f2966d52e536404ce35ad1096465f9
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
@@ -244,7 +244,6 @@ module Vinter
244
244
  if !current_token
245
245
  return nil
246
246
  end
247
- start_token = current_token
248
247
 
249
248
  # Handle pipe as command separator
250
249
  if current_token[:type] == :operator && current_token[:value] == '|'
@@ -378,7 +377,7 @@ module Vinter
378
377
  parse_set_command
379
378
  when 'syntax'
380
379
  parse_syntax_command
381
- when 'highlight'
380
+ when 'highlight', 'hi'
382
381
  parse_highlight_command
383
382
  when 'sleep'
384
383
  parse_sleep_command
@@ -425,23 +424,23 @@ module Vinter
425
424
  elsif current_token[:type] == :percentage
426
425
  parse_range_command
427
426
  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
-
427
+ parse_global_variable
428
+ elsif current_token[:type] == :buffer_local
429
+ parse_buffer_var
442
430
  elsif current_token[:type] == :vimfuncs
443
431
  advance
444
432
  parse_function_call(current_token[:value], current_token[:line], current_token[:column])
433
+ elsif current_token[:type] == :builtin_funcs
434
+ parse_builtin_function_call
435
+ elsif current_token[:type] == :compound_operator
436
+ advance
437
+ parse_expression
438
+ elsif current_token[:type] == :string
439
+ parse_string
440
+ elsif current_token[:type] == :comma
441
+ advance
442
+ elsif current_token[:type] == :colon
443
+ parse_command_line_call
445
444
  else
446
445
  @warnings << {
447
446
  message: "Unexpected token type: #{current_token[:type]}",
@@ -454,6 +453,182 @@ module Vinter
454
453
  end
455
454
  end
456
455
 
456
+ def parse_command_line_call
457
+ token = advance # Skip the ':'
458
+ line = token[:line]
459
+ column = token[:column]
460
+
461
+ # Extract the command or range
462
+ command = token[:value][1..] # Remove the leading ':'
463
+
464
+ # Check if the command is a range (e.g., `:2`, `:$`)
465
+ if command.match?(/^\d+$/) || command == '$'
466
+ return {
467
+ type: :range_command,
468
+ range: command,
469
+ line: line,
470
+ column: column
471
+ }
472
+ end
473
+
474
+ # Otherwise, it's a regular command (e.g., `:exe`)
475
+ args = []
476
+ while current_token && current_token[:type] != :newline
477
+ args << current_token[:value]
478
+ advance
479
+ end
480
+
481
+ {
482
+ type: :command_line_call,
483
+ command: command,
484
+ arguments: args,
485
+ line: line,
486
+ column: column
487
+ }
488
+ end
489
+
490
+ def parse_buffer_var
491
+ token = current_token
492
+ line = token[:line]
493
+ column = token[:column]
494
+ name = token[:value]
495
+ advance
496
+
497
+ # Handle property access (e.g., b:foo.bar)
498
+ target = {
499
+ type: :buffer_variable,
500
+ name: name,
501
+ line: line,
502
+ column: column
503
+ }
504
+
505
+ # Property access (dot notation)
506
+ # shared with global (TODO)
507
+ # would almost just take an overly simple check to the = operator and call everything after b: name
508
+ if current_token && current_token[:type] == :operator && current_token[:value] == '.'
509
+ dot_token = advance
510
+ if current_token && (current_token[:type] == :identifier || current_token[:type] == :keyword)
511
+ property_token = advance
512
+ target = {
513
+ type: :property_access,
514
+ object: target,
515
+ property: property_token[:value],
516
+ line: dot_token[:line],
517
+ column: dot_token[:column]
518
+ }
519
+ else
520
+ add_error("Expected property name after '.' in variable")
521
+ end
522
+ end
523
+
524
+ # Indexed access (e.g., b:foo[bar])
525
+ while current_token && current_token[:type] == :bracket_open
526
+ bracket_token = advance
527
+ index_expr = parse_expression
528
+ expect(:bracket_close)
529
+ target = {
530
+ type: :indexed_access,
531
+ object: target,
532
+ index: index_expr,
533
+ line: bracket_token[:line],
534
+ column: bracket_token[:column]
535
+ }
536
+ end
537
+
538
+ # Assignment operator (= or compound)
539
+ operator = nil
540
+ if current_token && (
541
+ (current_token[:type] == :operator && current_token[:value] == '=') ||
542
+ current_token[:type] == :compound_operator)
543
+ operator = current_token[:value]
544
+ advance
545
+ else
546
+ add_error("Expected assignment operator after global variable")
547
+ end
548
+
549
+ # Value expression
550
+ value = parse_expression
551
+
552
+ {
553
+ type: :buffer_variable_assignment,
554
+ target: target,
555
+ operator: operator,
556
+ value: value,
557
+ line: line,
558
+ column: column
559
+ }
560
+ end
561
+
562
+ def parse_global_variable
563
+ token = current_token
564
+ line = token[:line]
565
+ column = token[:column]
566
+ name = token[:value]
567
+ advance # Skip the global variable token
568
+
569
+ # Handle property access (e.g., g:foo.bar)
570
+ target = {
571
+ type: :global_variable,
572
+ name: name,
573
+ line: line,
574
+ column: column
575
+ }
576
+
577
+ # Property access (dot notation)
578
+ if current_token && current_token[:type] == :operator && current_token[:value] == '.'
579
+ dot_token = advance
580
+ if current_token && (current_token[:type] == :identifier || current_token[:type] == :keyword)
581
+ property_token = advance
582
+ target = {
583
+ type: :property_access,
584
+ object: target,
585
+ property: property_token[:value],
586
+ line: dot_token[:line],
587
+ column: dot_token[:column]
588
+ }
589
+ else
590
+ add_error("Expected property name after '.' in global variable")
591
+ end
592
+ end
593
+
594
+ # Indexed access (e.g., g:foo[bar])
595
+ while current_token && current_token[:type] == :bracket_open
596
+ bracket_token = advance
597
+ index_expr = parse_expression
598
+ expect(:bracket_close)
599
+ target = {
600
+ type: :indexed_access,
601
+ object: target,
602
+ index: index_expr,
603
+ line: bracket_token[:line],
604
+ column: bracket_token[:column]
605
+ }
606
+ end
607
+
608
+ # Assignment operator (= or compound)
609
+ operator = nil
610
+ if current_token && (
611
+ (current_token[:type] == :operator && current_token[:value] == '=') ||
612
+ current_token[:type] == :compound_operator)
613
+ operator = current_token[:value]
614
+ advance
615
+ else
616
+ add_error("Expected assignment operator after global variable")
617
+ end
618
+
619
+ # Value expression
620
+ value = parse_expression
621
+
622
+ {
623
+ type: :global_variable_assignment,
624
+ target: target,
625
+ operator: operator,
626
+ value: value,
627
+ line: line,
628
+ column: column
629
+ }
630
+ end
631
+
457
632
  def parse_range_command
458
633
  token = advance # Skip '%'
459
634
  line = token[:line]
@@ -847,9 +1022,11 @@ module Vinter
847
1022
  event = nil
848
1023
  if current_token && current_token[:type] == :identifier
849
1024
  event = advance[:value]
1025
+ elsif current_token[:type] == :operator && current_token[:value] == "!"
1026
+ advance
850
1027
  else
851
1028
  @errors << {
852
- message: "Expected event name after 'autocmd'",
1029
+ message: "Expected event name after 'autocmd' | #{current_token[:type]}",
853
1030
  position: @position,
854
1031
  line: current_token ? current_token[:line] : 0,
855
1032
  column: current_token ? current_token[:column] : 0
@@ -1117,7 +1294,7 @@ module Vinter
1117
1294
 
1118
1295
  # Parse optional return type
1119
1296
  return_type = nil
1120
- if current_token && current_token[:type] == :colon
1297
+ if current_token && current_token[:type] == :colon && current_token[:line] == line
1121
1298
  advance # Skip ':'
1122
1299
  return_type = parse_type
1123
1300
  end
@@ -1175,12 +1352,6 @@ module Vinter
1175
1352
  break
1176
1353
  end
1177
1354
 
1178
- # Get parameter name
1179
- if !current_token || current_token[:type] != :identifier
1180
- add_error("Expected parameter name")
1181
- break
1182
- end
1183
-
1184
1355
  param_name = advance
1185
1356
 
1186
1357
  # Check for type annotation
@@ -1270,7 +1441,7 @@ module Vinter
1270
1441
 
1271
1442
  # Parse initializer if present
1272
1443
  initializer = nil
1273
- if current_token && current_token[:type] == :operator && current_token[:value] == '='
1444
+ if current_token && (current_token[:type] == :operator && current_token[:value] == '=')
1274
1445
  advance # Skip '='
1275
1446
  initializer = parse_expression
1276
1447
  end
@@ -1563,7 +1734,7 @@ module Vinter
1563
1734
  # Special handling for map-related keywords when they appear in expressions
1564
1735
  if ['map', 'nmap', 'imap', 'vmap', 'xmap', 'noremap', 'nnoremap', 'inoremap', 'vnoremap', 'xnoremap', 'cnoremap', 'cmap'].include?(token[:value])
1565
1736
  if peek_token[:type] == :paren_open
1566
- parse_builtin_function_call(token[:value], line, column)
1737
+ parse_builtin_function_call
1567
1738
  else
1568
1739
  # Treat map commands as identifiers when inside expressions
1569
1740
  advance
@@ -1576,7 +1747,7 @@ module Vinter
1576
1747
  end
1577
1748
  elsif token[:value] == 'type' && current_token && (current_token[:type] == :paren_open || peek_token && peek_token[:type] == :paren_open)
1578
1749
  # This is the type() function call
1579
- return parse_builtin_function_call(token[:value], line, column)
1750
+ return parse_builtin_function_call
1580
1751
  elsif token[:value] == 'function'
1581
1752
  # Check if this is a function() call or just 'function' as property name
1582
1753
  if peek_token && peek_token[:type] == :paren_open
@@ -1662,6 +1833,8 @@ module Vinter
1662
1833
  line: line,
1663
1834
  column: column
1664
1835
  }
1836
+ elsif token[:value] == 'command'
1837
+ advance
1665
1838
  else
1666
1839
  @errors << {
1667
1840
  message: "Unexpected keyword in expression: #{token[:value]}",
@@ -1789,7 +1962,7 @@ module Vinter
1789
1962
  when :identifier
1790
1963
  # Special handling for Vim built-in functions
1791
1964
  if ['has', 'exists', 'empty', 'get', 'type', 'map', 'copy'].include?(token[:value])
1792
- expr = parse_builtin_function_call(token[:value], line, column)
1965
+ expr = parse_builtin_function_call
1793
1966
  else
1794
1967
  advance
1795
1968
 
@@ -1865,7 +2038,8 @@ module Vinter
1865
2038
  advance
1866
2039
  expr = parse_expression
1867
2040
  when :interpolated_string
1868
- advance
2041
+ token = advance
2042
+ value = token[:value]
1869
2043
  expr = {
1870
2044
  type: :interpolated_string,
1871
2045
  value: token[:value],
@@ -1926,6 +2100,7 @@ module Vinter
1926
2100
  # Check for method call with arrow ->
1927
2101
  elsif current_token[:type] == :operator && current_token[:value] == '->'
1928
2102
  arrow_token = advance # Skip '->'
2103
+ advance if current_token[:type] == :line_continuation
1929
2104
 
1930
2105
  # Next token should be an identifier (method name)
1931
2106
  if !current_token || current_token[:type] != :identifier
@@ -1990,6 +2165,12 @@ module Vinter
1990
2165
  else
1991
2166
  end_index = parse_expression
1992
2167
  end
2168
+ # handles array assignment [a,b] = split(thing)
2169
+ elsif current_token[:type] == :comma
2170
+ while current_token[:type] == :comma
2171
+ advance
2172
+ parse_expression
2173
+ end
1993
2174
  end
1994
2175
 
1995
2176
  expect(:bracket_close) # Skip ']'
@@ -2052,76 +2233,19 @@ module Vinter
2052
2233
  return expr
2053
2234
  end
2054
2235
 
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
2236
+ def parse_builtin_function_call
2237
+ token = advance # Consume the `builtin_funcs` token
2238
+ line = token[:line]
2239
+ column = token[:column]
2240
+ name = token[:value]
2106
2241
 
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]
2242
+ # Collect arguments (if any) until the end of the line
2243
+ args = []
2244
+ while current_token && current_token[:type] != :comment && current_token[:value] != "\n"
2245
+ args << current_token[:value]
2246
+ advance
2122
2247
  end
2123
2248
 
2124
- # Return function call node
2125
2249
  {
2126
2250
  type: :builtin_function_call,
2127
2251
  name: name,
@@ -2680,42 +2804,30 @@ module Vinter
2680
2804
 
2681
2805
  args = []
2682
2806
 
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
2807
  # Parse arguments until we find a closing parenthesis
2688
2808
  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
2809
+ # Skip line continuations
2810
+ if current_token[:type] == :line_continuation
2691
2811
  advance
2692
2812
  next
2693
2813
  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
2814
 
2710
- # Break if we hit the closing paren
2815
+ # Parse the argument
2816
+ arg = parse_expression
2817
+ args << arg if arg
2818
+
2819
+ # Break if we hit the closing parenthesis
2711
2820
  if current_token && current_token[:type] == :paren_close
2712
2821
  break
2713
2822
  end
2823
+
2714
2824
  # If we have a comma, advance past it and continue
2715
2825
  if current_token && current_token[:type] == :comma
2716
2826
  advance
2827
+ elsif current_token[:type] == :line_continuation
2828
+ advance
2717
2829
  # 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
2830
+ elsif current_token && current_token[:type] != :paren_close
2719
2831
  @errors << {
2720
2832
  message: "Expected comma or closing parenthesis after argument",
2721
2833
  position: @position,
@@ -2966,7 +3078,7 @@ module Vinter
2966
3078
  advance # Skip the '='
2967
3079
 
2968
3080
  # Get the value (number or identifier)
2969
- if current_token && (current_token[:type] == :number || current_token[:type] == :identifier)
3081
+ if current_token && (current_token[:type] == :number || current_token[:type] == :identifier) || current_token[:type] == :operator
2970
3082
  attribute += "=#{current_token[:value]}"
2971
3083
  attributes << attribute
2972
3084
  advance
@@ -2976,6 +3088,7 @@ module Vinter
2976
3088
 
2977
3089
  # Parse the command name
2978
3090
  command_name = nil
3091
+ advance if current_token[:type] == :line_continuation
2979
3092
  if current_token && current_token[:type] == :identifier
2980
3093
  command_name = current_token[:value]
2981
3094
  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.1"
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.1
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: []