v2 / vlib / v / token / token.v
717 lines · 682 sloc · 18.13 KB · 0f3332b1f3c761cd5215d968b3afef24cd3dc3cb
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module token
5
6const orm_custom_operators = ['like', 'ilike']
7
8@[minify]
9pub struct Token {
10pub:
11 kind Kind // the token number/enum; for quick comparisons
12 lit string // literal representation of the token
13 line_nr int // the line number in the source where the token occurred
14 col u16 // the column in the source where the token occurred
15 file_idx i16 // file idx in the global table `filelist`
16 pos int // the position of the token in scanner text
17 len int // length of the literal
18 tidx int // the index of the token
19}
20
21pub enum Kind {
22 unknown
23 eof
24 name // user
25 number // 123
26 string // 'foo'
27 str_inter // 'name=${user.name}'
28 chartoken // `A` - rune
29 plus // +
30 minus // -
31 mul // *
32 power // **
33 div // /
34 mod // %
35 xor // ^
36 pipe // |
37 inc // ++
38 dec // --
39 and // &&
40 logical_or // ||
41 not // !
42 bit_not // ~
43 question // ?
44 comma // ,
45 semicolon // ;
46 colon // :
47 arrow // <-
48 amp // &
49 hash // #
50 dollar // $
51 at // @
52 str_dollar
53 left_shift // <<
54 right_shift // >>
55 unsigned_right_shift // >>>
56 not_in // !in
57 not_is // !is
58 assign // =
59 decl_assign // :=
60 plus_assign // +=
61 minus_assign // -=
62 div_assign // /=
63 mult_assign // *=
64 power_assign // **=
65 xor_assign // ^=
66 mod_assign // %=
67 or_assign // |=
68 and_assign // &=
69 right_shift_assign // <<=
70 left_shift_assign // >>=
71 unsigned_right_shift_assign // >>>=
72 boolean_and_assign // &&=
73 boolean_or_assign // ||=
74 lcbr // {
75 rcbr // }
76 lpar // (
77 rpar // )
78 lsbr // [
79 nilsbr // #[
80 rsbr // ]
81 eq // ==
82 ne // !=
83 gt // >
84 lt // <
85 ge // >=
86 le // <=
87 comment
88 nl
89 dot // .
90 dotdot // ..
91 ellipsis // ...
92 keyword_beg
93 key_as
94 key_asm
95 key_assert
96 key_atomic
97 key_break
98 key_const
99 key_continue
100 key_defer
101 key_else
102 key_enum
103 key_false
104 key_for
105 key_fn
106 key_global
107 key_go
108 key_goto
109 key_if
110 key_import
111 key_in
112 key_interface
113 key_is
114 key_match
115 key_module
116 key_mut
117 key_nil
118 key_shared
119 key_lock
120 key_rlock
121 key_none
122 key_return
123 key_select
124 key_like
125 key_ilike
126 key_sizeof
127 key_isreftype
128 key_likely
129 key_unlikely
130 key_offsetof
131 key_struct
132 key_true
133 key_type
134 key_typeof
135 key_dump
136 key_orelse
137 key_union
138 key_pub
139 key_static
140 key_volatile
141 key_unsafe
142 key_spawn
143 key_implements
144 keyword_end
145 _end_
146}
147
148// @FN => will be substituted with the name of the current V function
149// @METHOD => will be substituted with ReceiverType.MethodName
150// @MOD => will be substituted with the name of the current V module
151// @STRUCT => will be substituted with the name of the current V struct
152// @FILE => will be substituted with the path of the V source file
153// @LINE => will be substituted with the V line number where it appears (as a string).
154// @COLUMN => will be substituted with the column where it appears (as a string).
155// @VHASH => will be substituted with the shortened commit hash of the V compiler (as a string).
156// @VMOD_FILE => will be substituted with the contents of the nearest v.mod file (as a string).
157// @VMODROOT => will be substituted with the *folder* where the nearest v.mod file is (as a string).
158// @VEXE => will be substituted with the path to the V compiler
159// @VEXEROOT => will be substituted with the *folder* where the V executable is (as a string).
160// @VROOT => the old name for @VMODROOT; sometimes it was used as @VEXEROOT;
161// Note: @VROOT is now deprecated, use either @VMODROOT or @VEXEROOT instead.
162// Note: @VEXEROOT & @VMODROOT are used for compilation options like this:
163// #include "@VMODROOT/include/abc.h"
164// #flag -L@VEXEROOT/thirdparty/libgc
165//
166// The @XYZ tokens allow for code like this:
167// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN)
168// ... which is useful while debugging/tracing.
169//
170// @<type> is allowed for keyword variable names. E.g. 'type'
171pub enum AtKind {
172 unknown
173 fn_name
174 method_name
175 mod_name
176 struct_name
177 vexe_path
178 file_path
179 file_dir
180 line_nr
181 column_nr
182 vhash
183 v_current_hash
184 vmod_file
185 vmodroot_path
186 vmod_hash
187 vroot_path // obsolete
188 vexeroot_path
189 file_path_line_nr
190 location
191 build_date
192 build_time
193 build_timestamp
194 os
195 ccompiler
196 backend
197 platform
198}
199
200pub const assign_tokens = [Kind.assign, .decl_assign, .plus_assign, .minus_assign, .mult_assign,
201 .power_assign, .div_assign, .xor_assign, .mod_assign, .or_assign, .and_assign,
202 .right_shift_assign, .left_shift_assign, .unsigned_right_shift_assign, .boolean_and_assign,
203 .boolean_or_assign]
204
205pub const valid_at_tokens = ['@VROOT', '@VMODROOT', '@VEXEROOT', '@FN', '@METHOD', '@MOD', '@STRUCT',
206 '@VEXE', '@FILE', '@DIR', '@LINE', '@COLUMN', '@VHASH', '@VCURRENTHASH', '@VMOD_FILE',
207 '@VMODHASH', '@FILE_LINE', '@LOCATION', '@BUILD_DATE', '@BUILD_TIME', '@BUILD_TIMESTAMP', '@OS',
208 '@CCOMPILER', '@BACKEND', '@PLATFORM']
209
210pub const token_str = build_token_str()
211
212pub const keywords = build_keys()
213
214pub const scanner_matcher = new_keywords_matcher_trie[Kind](keywords)
215
216// build_keys generates a map with keywords' string values:
217// Keywords['return'] == .key_return
218@[direct_array_access]
219fn build_keys() map[string]Kind {
220 mut res := map[string]Kind{}
221 for t in int(Kind.keyword_beg) + 1 .. int(Kind.keyword_end) {
222 key := token_str[t]
223
224 // Exclude custom ORM operators from V keyword list
225 if key in orm_custom_operators {
226 continue
227 }
228
229 res[key] = unsafe { Kind(t) }
230 }
231 return res
232}
233
234// TODO: remove once we have `enum Kind { name('name') if('if') ... }`
235@[direct_array_access]
236fn build_token_str() []string {
237 mut s := []string{len: int(Kind._end_)}
238 s[Kind.unknown] = 'unknown'
239 s[Kind.eof] = 'eof'
240 s[Kind.name] = 'name'
241 s[Kind.number] = 'number'
242 s[Kind.string] = 'string'
243 s[Kind.chartoken] = 'char'
244 s[Kind.plus] = '+'
245 s[Kind.minus] = '-'
246 s[Kind.mul] = '*'
247 s[Kind.power] = '**'
248 s[Kind.div] = '/'
249 s[Kind.mod] = '%'
250 s[Kind.xor] = '^'
251 s[Kind.bit_not] = '~'
252 s[Kind.pipe] = '|'
253 s[Kind.hash] = '#'
254 s[Kind.amp] = '&'
255 s[Kind.inc] = '++'
256 s[Kind.dec] = '--'
257 s[Kind.and] = '&&'
258 s[Kind.logical_or] = '||'
259 s[Kind.not] = '!'
260 s[Kind.dot] = '.'
261 s[Kind.dotdot] = '..'
262 s[Kind.ellipsis] = '...'
263 s[Kind.comma] = ','
264 s[Kind.not_in] = '!in'
265 s[Kind.not_is] = '!is'
266 s[Kind.semicolon] = ';'
267 s[Kind.colon] = ':'
268 s[Kind.arrow] = '<-'
269 s[Kind.assign] = '='
270 s[Kind.decl_assign] = ':='
271 s[Kind.plus_assign] = '+='
272 s[Kind.minus_assign] = '-='
273 s[Kind.mult_assign] = '*='
274 s[Kind.power_assign] = '**='
275 s[Kind.div_assign] = '/='
276 s[Kind.xor_assign] = '^='
277 s[Kind.mod_assign] = '%='
278 s[Kind.or_assign] = '|='
279 s[Kind.and_assign] = '&='
280 s[Kind.right_shift_assign] = '>>='
281 s[Kind.unsigned_right_shift_assign] = '>>>='
282 s[Kind.left_shift_assign] = '<<='
283 s[Kind.boolean_or_assign] = '||='
284 s[Kind.boolean_and_assign] = '&&='
285 s[Kind.lcbr] = '{'
286 s[Kind.rcbr] = '}'
287 s[Kind.lpar] = '('
288 s[Kind.rpar] = ')'
289 s[Kind.lsbr] = '['
290 s[Kind.nilsbr] = '#['
291 s[Kind.rsbr] = ']'
292 s[Kind.eq] = '=='
293 s[Kind.ne] = '!='
294 s[Kind.gt] = '>'
295 s[Kind.lt] = '<'
296 s[Kind.ge] = '>='
297 s[Kind.le] = '<='
298 s[Kind.question] = '?'
299 s[Kind.left_shift] = '<<'
300 s[Kind.right_shift] = '>>'
301 s[Kind.unsigned_right_shift] = '>>>'
302 s[Kind.comment] = 'comment'
303 s[Kind.nl] = 'NLL'
304 s[Kind.dollar] = '$'
305 s[Kind.at] = '@'
306 s[Kind.str_dollar] = '$2'
307 s[Kind.key_assert] = 'assert'
308 s[Kind.key_struct] = 'struct'
309 s[Kind.key_if] = 'if'
310 // s[Kind.key_it] = 'it'
311 s[Kind.key_else] = 'else'
312 s[Kind.key_asm] = 'asm'
313 s[Kind.key_return] = 'return'
314 s[Kind.key_module] = 'module'
315 s[Kind.key_sizeof] = 'sizeof'
316 s[Kind.key_isreftype] = 'isreftype'
317 s[Kind.key_likely] = '_likely_'
318 s[Kind.key_unlikely] = '_unlikely_'
319 s[Kind.key_go] = 'go'
320 s[Kind.key_goto] = 'goto'
321 s[Kind.key_const] = 'const'
322 s[Kind.key_mut] = 'mut'
323 s[Kind.key_shared] = 'shared'
324 s[Kind.key_lock] = 'lock'
325 s[Kind.key_rlock] = 'rlock'
326 s[Kind.key_type] = 'type'
327 s[Kind.key_for] = 'for'
328 s[Kind.key_fn] = 'fn'
329 s[Kind.key_true] = 'true'
330 s[Kind.key_false] = 'false'
331 s[Kind.key_continue] = 'continue'
332 s[Kind.key_break] = 'break'
333 s[Kind.key_import] = 'import'
334 s[Kind.key_unsafe] = 'unsafe'
335 s[Kind.key_typeof] = 'typeof'
336 s[Kind.key_dump] = 'dump'
337 s[Kind.key_enum] = 'enum'
338 s[Kind.key_interface] = 'interface'
339 s[Kind.key_pub] = 'pub'
340 s[Kind.key_in] = 'in'
341 s[Kind.key_atomic] = 'atomic'
342 s[Kind.key_orelse] = 'or'
343 s[Kind.key_global] = '__global'
344 s[Kind.key_union] = 'union'
345 s[Kind.key_static] = 'static'
346 s[Kind.key_volatile] = 'volatile'
347 s[Kind.key_as] = 'as'
348 s[Kind.key_defer] = 'defer'
349 s[Kind.key_match] = 'match'
350 s[Kind.key_select] = 'select'
351 s[Kind.key_like] = 'like'
352 s[Kind.key_ilike] = 'ilike'
353 s[Kind.key_none] = 'none'
354 s[Kind.key_nil] = 'nil'
355 s[Kind.key_offsetof] = '__offsetof'
356 s[Kind.key_is] = 'is'
357 s[Kind.key_spawn] = 'spawn'
358 s[Kind.key_implements] = 'implements'
359 // The following kinds are not for tokens returned by the V scanner.
360 // They are used just for organisation/ease of checking:
361 s[Kind.keyword_beg] = 'keyword_beg'
362 s[Kind.keyword_end] = 'keyword_end'
363 s[Kind.str_inter] = 'str_inter'
364 $if debug_build_token_str ? {
365 for k, v in s {
366 if v == '' {
367 eprintln('>>> ${@MOD}.${@METHOD} missing k: ${k} | .${kind_to_string(unsafe { Kind(k) })}')
368 }
369 }
370 }
371 return s
372}
373
374@[inline]
375pub fn is_key(key string) bool {
376 return int(keywords[key]) > 0
377}
378
379@[inline]
380pub fn is_decl(t Kind) bool {
381 return t in [.key_enum, .key_interface, .key_fn, .key_struct, .key_type, .key_const, .key_pub,
382 .eof]
383}
384
385@[inline]
386pub fn (t Kind) is_assign() bool {
387 return t in assign_tokens
388}
389
390// note: used for some code generation, so no quoting
391@[direct_array_access; inline]
392pub fn (t Kind) str() string {
393 idx := int(t)
394 if idx < 0 || token_str.len <= idx {
395 return 'unknown'
396 }
397 return token_str[idx]
398}
399
400@[inline]
401pub fn (t Token) is_next_to(pre_token Token) bool {
402 return t.pos - pre_token.pos == pre_token.len
403}
404
405@[inline]
406pub fn (t Token) is_key() bool {
407 return int(t.kind) > int(Kind.keyword_beg) && int(t.kind) < int(Kind.keyword_end)
408}
409
410@[direct_array_access]
411pub fn (t Token) str() string {
412 mut s := t.kind.str()
413 if s.len == 0 {
414 eprintln('missing token kind string')
415 } else if !s[0].is_letter() {
416 // punctuation, operators
417 return 'token `${s}`'
418 }
419 if is_key(t.lit) {
420 s = 'keyword'
421 }
422 if t.lit != '' {
423 // string contents etc
424 s += ' `${t.lit}`'
425 }
426 return s
427}
428
429pub fn (t Token) debug() string {
430 ks := kind_to_string(t.kind)
431 s := if t.lit == '' { t.kind.str() } else { t.lit }
432 return 'tok: .${ks:-12} | lit: `${s}`'
433}
434
435// Representation of highest and lowest precedence
436pub enum Precedence {
437 lowest
438 cond // OR or AND
439 in_as
440 assign // =
441 eq // == or !=
442 // less_greater // > or <
443 sum // + - | ^
444 product // * / << >> >>> &
445 // mod // %
446 prefix // -X or !X; TODO: seems unused
447 power // **
448 postfix // ++ or --
449 call // func(X) or foo.method(X)
450 index // array[index], map[key]
451 highest
452}
453
454@[direct_array_access]
455pub fn build_precedences() []Precedence {
456 mut p := []Precedence{len: int(Kind._end_), init: Precedence.lowest}
457 p[Kind.lsbr] = .index
458 p[Kind.nilsbr] = .index
459 p[Kind.dot] = .call
460 // `++` | `--` | `?`
461 p[Kind.inc] = .postfix
462 p[Kind.dec] = .postfix
463 p[Kind.question] = .postfix
464 // `*` | `/` | `%` | `<<` | `>>` | `>>>` | `&`
465 p[Kind.mul] = .product
466 p[Kind.div] = .product
467 p[Kind.mod] = .product
468 p[Kind.left_shift] = .product
469 p[Kind.right_shift] = .product
470 p[Kind.unsigned_right_shift] = .product
471 p[Kind.amp] = .product
472 p[Kind.arrow] = .product
473 p[Kind.power] = .power
474 // `+` | `-` | `|` | `^`
475 p[Kind.plus] = .sum
476 p[Kind.minus] = .sum
477 p[Kind.pipe] = .sum
478 p[Kind.xor] = .sum
479 // `==` | `!=` | `<` | `<=` | `>` | `>=` | `like`
480 p[Kind.eq] = .eq
481 p[Kind.ne] = .eq
482 p[Kind.lt] = .eq
483 p[Kind.le] = .eq
484 p[Kind.gt] = .eq
485 p[Kind.ge] = .eq
486 p[Kind.key_like] = .eq
487 p[Kind.key_ilike] = .eq
488 // `=` | `+=` | ...
489 p[Kind.assign] = .assign
490 p[Kind.plus_assign] = .assign
491 p[Kind.minus_assign] = .assign
492 p[Kind.power_assign] = .assign
493 p[Kind.div_assign] = .assign
494 p[Kind.mod_assign] = .assign
495 p[Kind.or_assign] = .assign
496 p[Kind.and_assign] = .assign
497 // <<= | *= | ...
498 p[Kind.left_shift_assign] = .assign
499 p[Kind.right_shift_assign] = .assign
500 p[Kind.unsigned_right_shift_assign] = .assign
501 p[Kind.mult_assign] = .assign
502 p[Kind.xor_assign] = .assign
503 p[Kind.boolean_or_assign] = .assign
504 p[Kind.boolean_and_assign] = .assign
505 p[Kind.key_in] = .in_as
506 p[Kind.not_in] = .in_as
507 p[Kind.key_as] = .in_as
508 p[Kind.key_is] = .in_as
509 p[Kind.not_is] = .in_as
510 p[Kind.logical_or] = .cond
511 p[Kind.and] = .cond
512 return p
513}
514
515const precedences = build_precedences()
516
517// precedence returns a tokens precedence if defined, otherwise 0
518@[direct_array_access; inline]
519pub fn (tok Token) precedence() int {
520 return int(precedences[tok.kind])
521}
522
523// precedence returns the precedence of the given token `kind` if defined, otherwise 0
524@[direct_array_access; inline]
525pub fn (kind Kind) precedence() int {
526 return int(precedences[kind])
527}
528
529// is_scalar returns true if the token is a scalar
530@[inline]
531pub fn (tok Token) is_scalar() bool {
532 return tok.kind in [.number, .string]
533}
534
535// is_unary returns true if the token can be in a unary expression
536@[inline]
537pub fn (tok Token) is_unary() bool {
538 // `+` | `-` | `!` | `~` | `*` | `&` | `<-`
539 return tok.kind in [.plus, .minus, .not, .bit_not, .mul, .amp, .arrow]
540}
541
542@[inline]
543pub fn (tok Kind) is_relational() bool {
544 // `<` | `<=` | `>` | `>=` | `==` | `!=`
545 return tok in [.lt, .le, .gt, .ge, .eq, .ne]
546}
547
548@[inline]
549pub fn (k Kind) is_start_of_type() bool {
550 return k in [.name, .lpar, .amp, .lsbr, .question, .key_shared, .not]
551}
552
553@[inline]
554pub fn (kind Kind) is_prefix() bool {
555 return kind in [.minus, .amp, .mul, .not, .bit_not]
556}
557
558@[inline]
559pub fn (kind Kind) is_infix() bool {
560 return kind in [.plus, .minus, .mod, .mul, .power, .div, .eq, .ne, .gt, .lt, .key_in, .key_as,
561 .ge, .le, .logical_or, .xor, .not_in, .key_is, .not_is, .and, .dot, .pipe, .amp, .left_shift,
562 .right_shift, .unsigned_right_shift, .arrow, .key_like, .key_ilike]
563}
564
565@[inline]
566pub fn (kind Kind) is_postfix() bool {
567 return kind in [.inc, .dec, .question]
568}
569
570pub fn kind_to_string(k Kind) string {
571 return match k {
572 .unknown { 'unknown' }
573 .eof { 'eof' }
574 .name { 'name' }
575 .number { 'number' }
576 .string { 'string' }
577 .str_inter { 'str_inter' }
578 .chartoken { 'chartoken' }
579 .plus { 'plus' }
580 .minus { 'minus' }
581 .mul { 'mul' }
582 .power { 'power' }
583 .div { 'div' }
584 .mod { 'mod' }
585 .xor { 'xor' }
586 .pipe { 'pipe' }
587 .inc { 'inc' }
588 .dec { 'dec' }
589 .and { 'and' }
590 .logical_or { 'logical_or' }
591 .not { 'not' }
592 .bit_not { 'bit_not' }
593 .question { 'question' }
594 .comma { 'comma' }
595 .semicolon { 'semicolon' }
596 .colon { 'colon' }
597 .arrow { 'arrow' }
598 .amp { 'amp' }
599 .hash { 'hash' }
600 .dollar { 'dollar' }
601 .at { 'at' }
602 .str_dollar { 'str_dollar' }
603 .left_shift { 'left_shift' }
604 .right_shift { 'right_shift' }
605 .unsigned_right_shift { 'unsigned_right_shift' }
606 .not_in { 'not_in' }
607 .not_is { 'not_is' }
608 .assign { 'assign' }
609 .decl_assign { 'decl_assign' }
610 .plus_assign { 'plus_assign' }
611 .minus_assign { 'minus_assign' }
612 .div_assign { 'div_assign' }
613 .mult_assign { 'mult_assign' }
614 .power_assign { 'power_assign' }
615 .xor_assign { 'xor_assign' }
616 .mod_assign { 'mod_assign' }
617 .or_assign { 'or_assign' }
618 .and_assign { 'and_assign' }
619 .right_shift_assign { 'right_shift_assign' }
620 .left_shift_assign { 'left_shift_assign' }
621 .unsigned_right_shift_assign { 'unsigned_right_shift_assign' }
622 .boolean_and_assign { 'boolean_and_assign' }
623 .boolean_or_assign { 'boolean_or_assign' }
624 .lcbr { 'lcbr' }
625 .rcbr { 'rcbr' }
626 .lpar { 'lpar' }
627 .rpar { 'rpar' }
628 .lsbr { 'lsbr' }
629 .nilsbr { 'nilsbr' }
630 .rsbr { 'rsbr' }
631 .eq { 'eq' }
632 .ne { 'ne' }
633 .gt { 'gt' }
634 .lt { 'lt' }
635 .ge { 'ge' }
636 .le { 'le' }
637 .comment { 'comment' }
638 .nl { 'nl' }
639 .dot { 'dot' }
640 .dotdot { 'dotdot' }
641 .ellipsis { 'ellipsis' }
642 .keyword_beg { 'keyword_beg' }
643 .key_as { 'key_as' }
644 .key_asm { 'key_asm' }
645 .key_assert { 'key_assert' }
646 .key_atomic { 'key_atomic' }
647 .key_break { 'key_break' }
648 .key_const { 'key_const' }
649 .key_continue { 'key_continue' }
650 .key_defer { 'key_defer' }
651 .key_else { 'key_else' }
652 .key_enum { 'key_enum' }
653 .key_false { 'key_false' }
654 .key_for { 'key_for' }
655 .key_fn { 'key_fn' }
656 .key_global { 'key_global' }
657 .key_go { 'key_go' }
658 .key_goto { 'key_goto' }
659 .key_if { 'key_if' }
660 .key_import { 'key_import' }
661 .key_in { 'key_in' }
662 .key_interface { 'key_interface' }
663 .key_is { 'key_is' }
664 .key_match { 'key_match' }
665 .key_module { 'key_module' }
666 .key_mut { 'key_mut' }
667 .key_shared { 'key_shared' }
668 .key_lock { 'key_lock' }
669 .key_rlock { 'key_rlock' }
670 .key_none { 'key_none' }
671 .key_return { 'key_return' }
672 .key_select { 'key_select' }
673 .key_like { 'key_like' }
674 .key_ilike { 'key_ilike' }
675 .key_sizeof { 'key_sizeof' }
676 .key_isreftype { 'key_isreftype' }
677 .key_likely { 'key_likely' }
678 .key_unlikely { 'key_unlikely' }
679 .key_offsetof { 'key_offsetof' }
680 .key_struct { 'key_struct' }
681 .key_true { 'key_true' }
682 .key_type { 'key_type' }
683 .key_typeof { 'key_typeof' }
684 .key_dump { 'key_dump' }
685 .key_orelse { 'key_orelse' }
686 .key_union { 'key_union' }
687 .key_pub { 'key_pub' }
688 .key_static { 'key_static' }
689 .key_volatile { 'key_volatile' }
690 .key_unsafe { 'key_unsafe' }
691 .key_spawn { 'key_spawn' }
692 .key_implements { 'key_implements' }
693 .keyword_end { 'keyword_end' }
694 ._end_ { '_end_' }
695 .key_nil { 'key_nil' }
696 }
697}
698
699pub fn assign_op_to_infix_op(op Kind) Kind {
700 return match op {
701 .plus_assign { .plus }
702 .minus_assign { .minus }
703 .mult_assign { .mul }
704 .power_assign { .power }
705 .div_assign { .div }
706 .xor_assign { .xor }
707 .mod_assign { .mod }
708 .or_assign { .pipe }
709 .and_assign { .amp }
710 .right_shift_assign { .right_shift }
711 .unsigned_right_shift_assign { .unsigned_right_shift }
712 .left_shift_assign { .left_shift }
713 .boolean_and_assign { .and }
714 .boolean_or_assign { .logical_or }
715 else { ._end_ }
716 }
717}
718