OILS / frontend / syntax.asdl View on Github | oilshell.org

647 lines, 295 significant
1# Data types for the Oils AST, aka "Lossless Syntax Tree".
2#
3# Invariant: the source text can be reconstructed byte-for-byte from this tree.
4# The test/lossless.sh suite verifies this.
5
6# We usually try to preserve the physical order of the source in the ASDL
7# fields. One exception is the order of redirects:
8#
9# echo >out.txt hi
10# # versus
11# echo hi >out.txt
12
13# Unrepresented:
14# - let arithmetic (rarely used)
15# - coprocesses # one with arg and one without
16# - select block
17
18# Possible refactorings:
19# # Can DoubleQuoted have a subset of parts compared with CompoundWord?
20# string_part = ... # subset of word_part
21#
22# - Distinguish word_t with BracedTree vs. those without? seq_word_t?
23
24module syntax
25{
26 use core value {
27 value LiteralBlock
28 }
29
30 # More efficient than the List[bool] pattern we've been using
31 BoolParamBox = (bool b)
32 IntParamBox = (int i)
33
34 # core/main_loop.py
35 parse_result = EmptyLine | Eof | Node(command cmd)
36
37 # 'source' represents the location of a line / token.
38 source =
39 Interactive
40 | Headless
41 | Unused(str comment) # completion and history never show parse errors?
42 | CFlag
43 | Stdin(str comment)
44
45 # oshrc/ysh are considered a MainFile - loaded directly by the shell
46 | MainFile(str path)
47 # TODO: if it's not the main script, it's sourced or a module/ and you
48 # could provide a chain of locations back to the sourced script!
49 | OtherFile(str path, loc location)
50
51 # code parsed from a word
52 # used for 'eval', 'trap', 'printf', 'complete -W', parseCommand()
53 | Dynamic(str what, loc location)
54
55 # code parsed from the value of a variable
56 # used for $PS1 $PROMPT_COMMAND
57 | Variable(str var_name, loc location)
58
59 # Point to the original variable reference
60 | VarRef(Token orig_tok)
61
62 # alias expansion (location of first word)
63 | Alias(str argv0, loc argv0_loc)
64
65 # 2 kinds of reparsing: backticks, and x+1 in a[x+1]=y
66 # TODO: use this for eval_unsafe_arith instead of Variable
67 | Reparsed(str what, Token left_token, Token right_token)
68
69 # For --location-str
70 | Synthetic(str s)
71
72 SourceLine = (int line_num, str content, source src)
73
74 # Note that ASDL generates:
75 # typedef uint16_t Id_t;
76 # So Token is
77 # 8 bytes GC header + 2 + 2 + 4 + 8 + 8 = 32 bytes on 64-bit machines
78 #
79 # We transpose (id, col, length) -> (id, length, col) for C struct packing.
80 Token = (id id, uint16 length, int col, SourceLine? line, str? tval)
81
82 # I wanted to get rid of Token.tval with this separate WideToken type, but it
83 # is more efficient if word_part.Literal %Token literally is the same thing
84 # that comes out of the lexer. Otherwise we have extra garbage.
85
86 # WideToken = (id id, int length, int col, SourceLine? line, str? tval)
87
88 # Slight ASDL bug: CompoundWord has to be defined before using it as a shared
89 # variant. The _product_counter algorithm should be moved into a separate
90 # tag-assigning pass, and shared between gen_python.py and gen_cpp.py.
91 CompoundWord = (List[word_part] parts)
92
93 # Source location for errors
94 loc =
95 Missing # equivalent of runtime.NO_SPID
96 | Token %Token
97 # Very common case: argv arrays need original location
98 | ArgWord %CompoundWord
99 | WordPart(word_part p)
100 | Word(word w)
101 | Arith(arith_expr a)
102 # e.g. for errexit blaming
103 | Command(command c)
104 # the location of a token that's too long
105 | TokenTooLong(SourceLine line, id id, int length, int col)
106
107 debug_frame =
108 Main(str dollar0)
109 # call_loc => BASH_LINENO
110 # call_loc may be None with new --source flag?
111 | Source(Token? call_tok, str source_name)
112 # def_tok => BASH_SOURCE
113 # call_loc may be None if invoked via RunFuncForCompletion?
114 | Call(Token? call_tok, Token def_tok, str func_name)
115
116 #
117 # Shell language
118 #
119
120 bracket_op =
121 WholeArray(id op_id) # * or @
122 | ArrayIndex(arith_expr expr)
123
124 suffix_op =
125 Nullary %Token # ${x@Q} or ${!prefix@} (which also has prefix_op)
126 | Unary(Token op, rhs_word arg_word) # e.g. ${v:-default}
127 # TODO: Implement YSH ${x|html} and ${x %.3f}
128 | Static(Token tok, str arg)
129 | PatSub(CompoundWord pat, rhs_word replace, id replace_mode, Token slash_tok)
130 # optional begin is arith_expr.EmptyZero
131 # optional length is None, because it's handled in a special way
132 | Slice(arith_expr begin, arith_expr? length)
133
134 BracedVarSub = (
135 Token left, # in dynamic ParseVarRef, same as name_tok
136 Token token, # location for the name
137 str var_name, # the name - TODO: remove this, use LazyStr() instead
138 Token? prefix_op, # prefix # or ! operators
139 bracket_op? bracket_op,
140 suffix_op? suffix_op,
141 Token right # in dynamic ParseVarRef, same as name_tok
142 )
143
144 # Variants:
145 # - Look at left token ID for $'' c'' vs r'' '' e.g. Id.Left_DollarSingleQuote
146 # - And """ and ''' e.g. Id.Left_TDoubleQuote
147 DoubleQuoted = (Token left, List[word_part] parts, Token right)
148
149 # Consider making str? sval LAZY, like lexer.LazyStr(tok)
150 SingleQuoted = (Token left, str sval, Token right)
151
152 # e.g. Id.VSub_QMark, Id.VSub_DollarName $foo with lexer.LazyStr()
153 SimpleVarSub = (Token tok)
154
155 CommandSub = (Token left_token, command child, Token right)
156
157 # - can contain word.BracedTree
158 # - no 'Token right' for now, doesn't appear to be used
159 ShArrayLiteral = (Token left, List[word] words, Token right)
160
161 # Unevaluated, typed arguments for func and proc.
162 # Note that ...arg is expr.Spread.
163 ArgList = (
164 Token left, List[expr] pos_args,
165 Token? semi_tok, List[NamedArg] named_args,
166 Token? semi_tok2, expr? block_expr,
167 Token right
168 )
169
170 AssocPair = (CompoundWord key, CompoundWord value)
171
172 word_part =
173 ShArrayLiteral %ShArrayLiteral
174 | BashAssocLiteral(Token left, List[AssocPair] pairs, Token right)
175 | Literal %Token
176 # escaped case is separate so the evaluator doesn't have to check token ID
177 | EscapedLiteral(Token token, str ch)
178 | SingleQuoted %SingleQuoted
179 | DoubleQuoted %DoubleQuoted
180 # Could be SimpleVarSub %Token that's VSub_DollarName, but let's not
181 # confuse with the comon word_part.Literal is common for wno
182 | SimpleVarSub %SimpleVarSub
183 | BracedVarSub %BracedVarSub
184 | ZshVarSub (Token left, CompoundWord ignored, Token right)
185 # For command sub and process sub: $(...) <(...) >(...)
186 | CommandSub %CommandSub
187 # ~ or ~bob
188 | TildeSub(Token left, # always the tilde
189 Token? name, str? user_name)
190 | ArithSub(Token left, arith_expr anode, Token right)
191 # {a,b,c}
192 | BracedTuple(List[CompoundWord] words)
193 # {1..10} or {-5..10..2} or {01..10} (leading zeros matter)
194 # {a..f} or {a..f..2} or {a..f..-2}
195 # the whole range is one Token,
196 | BracedRange(Token blame_tok, id kind, str start, str end, int step)
197 # extended globs are parsed statically, unlike globs
198 | ExtGlob(Token op, List[CompoundWord] arms, Token right)
199 # a regex group is similar to an extended glob part
200 | BashRegexGroup(Token left, CompoundWord? child, Token right)
201
202 # YSH word_part extensions
203
204 # @myarray - Id.Lit_Splice (could be optimized to %Token)
205 | Splice(Token blame_tok, str var_name)
206 # $[d.key], etc.
207 | ExprSub(Token left, expr child, Token right)
208
209 # Use cases for Empty: RHS of 'x=', the argument in "${x:-}".
210 # The latter is semantically necessary. (See osh/word_parse.py).
211 # At runtime: RHS of 'declare x='.
212 rhs_word = Empty | Compound %CompoundWord
213
214 word =
215 # Returns from WordParser, but not generally stored in LST
216 Operator %Token
217 # A Compound word can contain any word_part except the Braced*Part.
218 # We could model this with another variant type but it incurs runtime
219 # overhead and seems like overkill. Note that DoubleQuoted can't
220 # contain a SingleQuoted, etc. either.
221 | Compound %CompoundWord
222 # For word sequences command.Simple, ShArrayLiteral, for_iter.Words
223 # Could be its own type
224 | BracedTree(List[word_part] parts)
225 # For dynamic parsing of test aka [ - the string is already evaluated.
226 | String(id id, str s, CompoundWord? blame_loc)
227
228 # Note: the name 'foo' is derived from token value 'foo=' or 'foo+='
229 sh_lhs =
230 Name(Token left, str name) # Lit_VarLike foo=
231 # TODO: Could be Name %Token
232 | IndexedName(Token left, str name, arith_expr index)
233 | UnparsedIndex(Token left, str name, str index) # for translation
234
235 arith_expr =
236 EmptyZero # these are valid: $(( )) (( )) ${a[@]: : }
237 | EmptyOne # condition is 1 for infinite loop: for (( ; ; ))
238 | VarSub %Token # e.g. $(( x )) Id.Arith_VarLike
239 | Word %CompoundWord # e.g. $(( 123'456'$y ))
240
241 | UnaryAssign(id op_id, arith_expr child)
242 | BinaryAssign(id op_id, arith_expr left, arith_expr right)
243
244 | Unary(id op_id, arith_expr child)
245 | Binary(Token op, arith_expr left, arith_expr right)
246 | TernaryOp(arith_expr cond, arith_expr true_expr, arith_expr false_expr)
247
248 bool_expr =
249 WordTest(word w) # e.g. [[ myword ]]
250 | Binary(id op_id, word left, word right)
251 | Unary(id op_id, word child)
252 | LogicalNot(bool_expr child)
253 | LogicalAnd(bool_expr left, bool_expr right)
254 | LogicalOr(bool_expr left, bool_expr right)
255
256 redir_loc =
257 Fd(int fd) | VarName(str name)
258
259 redir_param =
260 Word %CompoundWord
261 | HereWord(CompoundWord w, bool is_multiline)
262 | HereDoc(word here_begin, # e.g. EOF or 'EOF'
263 Token? here_end_tok, # Token consisting of the whole line
264 # It's always filled in AFTER creation, but
265 # temporarily so optional
266 List[word_part] stdin_parts # one for each line
267 )
268
269 Redir = (Token op, redir_loc loc, redir_param arg)
270
271 assign_op = Equal | PlusEqual
272 AssignPair = (Token left, sh_lhs lhs, assign_op op, rhs_word rhs)
273 # TODO: could put Id.Lit_VarLike foo= into LazyStr() with -1 slice
274 EnvPair = (Token left, str name, rhs_word val)
275
276 condition =
277 Shell(List[command] commands) # if false; true; then echo hi; fi
278 | YshExpr(expr e) # if (x > 0) { echo hi }
279 # TODO: add more specific blame location
280
281 # Each arm tests one word against multiple words
282 # shell: *.cc|*.h) echo C++ ;;
283 # YSH: *.cc|*.h { echo C++ }
284 #
285 # Three location tokens:
286 # 1. left - shell has ( or *.cc ysh has *.cc
287 # 2. middle - shell has ) ysh has {
288 # 3. right - shell has optional ;; ysh has required }
289 #
290 # For YSH typed case, left can be ( and /
291 # And case_pat may contain more details
292 CaseArm = (
293 Token left, pat pattern, Token middle, List[command] action,
294 Token? right
295 )
296
297 # The argument to match against in a case command
298 # In YSH-style case commands we match against an `expr`, but in sh-style case
299 # commands we match against a word.
300 case_arg =
301 Word(word w)
302 | YshExpr(expr e)
303
304 EggexFlag = (bool negated, Token flag)
305
306 # canonical_flags can be compared for equality. This is needed to splice
307 # eggexes correctly, e.g. / 'abc' @pat ; i /
308 Eggex = (
309 Token left, re regex, List[EggexFlag] flags, Token? trans_pref,
310 str? canonical_flags)
311
312 pat =
313 Else
314 | Words(List[word] words)
315 | YshExprs(List[expr] exprs)
316 | Eggex %Eggex
317
318 # Each if arm starts with either an "if" or "elif" keyword
319 # In YSH, the then keyword is not used (replaced by braces {})
320 IfArm = (
321 Token keyword, condition cond, Token? then_kw, List[command] action,
322 # then_tok used in ysh-ify
323 Token? then_tok)
324
325 for_iter =
326 Args # for x; do echo $x; done # implicit "$@"
327 | Words(List[word] words) # for x in 'foo' *.py { echo $x }
328 # like ShArrayLiteral, but no location for %(
329 | YshExpr(expr e, Token blame) # for x in (mylist) { echo $x }
330 #| Files(Token left, List[word] words)
331 # for x in <> {
332 # for x in < @myfiles > {
333
334 BraceGroup = (
335 Token left, Token? doc_token, List[command] children, Token right
336 )
337
338 Param = (Token blame_tok, str name, TypeExpr? type, expr? default_val)
339 RestParam = (Token blame_tok, str name)
340
341 ParamGroup = (List[Param] params, RestParam? rest_of)
342
343 # 'open' is for proc p { }; closed is for proc p () { }
344 proc_sig =
345 Open
346 | Closed(ParamGroup? word, ParamGroup? positional, ParamGroup? named,
347 Param? block_param)
348
349 Proc = (Token keyword, Token name, proc_sig sig, command body)
350
351 Func = (
352 Token keyword, Token name,
353 ParamGroup? positional, ParamGroup? named,
354 command body
355 )
356
357 # Represents all these case: s=1 s+=1 s[x]=1 ...
358 ParsedAssignment = (Token? left, Token? close, int part_offset, CompoundWord w)
359
360 command =
361 NoOp
362
363 # can wrap many children, e.g. { }, loops, functions
364 | Redirect(command child, List[Redir] redirects)
365
366 | Simple(Token? blame_tok, # TODO: make required (BracedTuple?)
367 List[EnvPair] more_env,
368 List[word] words,
369 ArgList? typed_args, LiteralBlock? block,
370 # is_last_cmd is used for fork() optimizations
371 bool is_last_cmd)
372
373 # This doesn't technically belong in the LST, but it's convenient for
374 # execution
375 | ExpandedAlias(command child, List[EnvPair] more_env)
376 | Sentence(command child, Token terminator)
377 # Represents "bare assignment"
378 # Token left is redundant with pairs[0].left
379 | ShAssignment(Token left, List[AssignPair] pairs)
380
381 | ControlFlow(Token keyword, word? arg_word)
382
383 # ops are | |&
384 | Pipeline(Token? negated, List[command] children, List[Token] ops)
385 # ops are && ||
386 | AndOr(List[command] children, List[Token] ops)
387
388 # Part of for, while, until (but not if, case, ShFunction). No redirects.
389 | DoGroup(Token left, List[command] children, Token right)
390 # A brace group is a compound command, with redirects.
391 | BraceGroup %BraceGroup
392 # Contains a single child, like CommandSub
393 | Subshell(Token left, command child, Token right, bool is_last_cmd)
394 | DParen(Token left, arith_expr child, Token right)
395 | DBracket(Token left, bool_expr expr, Token right)
396
397 # up to 3 iterations variables
398 | ForEach(Token keyword, List[str] iter_names, for_iter iterable,
399 Token? semi_tok, command body)
400 # C-style for loop. Any of the 3 expressions can be omitted.
401 # Note: body is required, but only optional here because of initialization
402 # order.
403 | ForExpr(Token keyword, arith_expr? init, arith_expr? cond,
404 arith_expr? update, command? body)
405 | WhileUntil(Token keyword, condition cond, command body)
406
407 | If(Token if_kw, List[IfArm] arms, Token? else_kw, List[command] else_action,
408 Token? fi_kw)
409 | Case(Token case_kw, case_arg to_match, Token arms_start, List[CaseArm] arms,
410 Token arms_end)
411
412 # The keyword is optional in the case of bash-style functions
413 # (ie. "foo() { ... }") which do not have one.
414 | ShFunction(Token? keyword, Token name_tok, str name, command body)
415
416 | TimeBlock(Token keyword, command pipeline)
417 # Some nodes optimize it out as List[command], but we use CommandList for
418 # 1. the top level
419 # 2. ls ; ls & ls (same line)
420 # 3. CommandSub # single child that's a CommandList
421 # 4. Subshell # single child that's a CommandList
422 | CommandList(List[command] children)
423
424 # YSH command constructs
425
426 # var, const.
427 # - Keyword is None for hay blocks
428 # - RHS is None, for use with value.Place
429 # - TODO: consider using BareDecl
430 | VarDecl(Token? keyword, List[NameType] lhs, expr? rhs)
431
432 # this can behave like 'var', can be desugared
433 | BareDecl(Token lhs, expr rhs)
434
435 # setvar, maybe 'auto' later
436 | Mutation(Token keyword, List[y_lhs] lhs, Token op, expr rhs)
437 # = keyword
438 | Expr(Token keyword, expr e)
439 | Proc %Proc
440 | Func %Func
441 | Retval(Token keyword, expr val)
442
443 #
444 # Glob representation, for converting ${x//} to extended regexes.
445 #
446
447 # Example: *.[ch] is:
448 # GlobOp(<Glob_Star '*'>),
449 # GlobLit(Glob_OtherLiteral, '.'),
450 # CharClass(False, ['ch']) # from Glob_CleanLiterals token
451
452 glob_part =
453 Literal(id id, str s)
454 | Operator(id op_id) # * or ?
455 | CharClass(bool negated, List[str] strs)
456
457 # Char classes are opaque for now. If we ever need them:
458 # - Collating symbols are [. .]
459 # - Equivalence classes are [=
460
461 printf_part =
462 Literal %Token
463 # flags are 0 hyphen space + #
464 # type is 's' for %s, etc.
465 | Percent(List[Token] flags, Token? width, Token? precision, Token type)
466
467 #
468 # YSH Language
469 #
470 # Copied and modified from Python-3.7/Parser/Python.asdl !
471
472 expr_context = Load | Store | Del | AugLoad | AugStore | Param
473
474 # Type expressions: Int List[Int] Dict[Str, Any]
475 # Do we have Func[Int, Int => Int] ? I guess we can parse that into this
476 # system.
477 TypeExpr = (Token tok, str name, List[TypeExpr] params)
478
479 # LHS bindings in var/const, and eggex
480 NameType = (Token left, str name, TypeExpr? typ)
481
482 # TODO: Inline this into GenExp and ListComp? Just use a flag there?
483 Comprehension = (List[NameType] lhs, expr iter, expr? cond)
484
485 # Named arguments supplied to call. Token is null for f(; ...named).
486 NamedArg = (Token? name, expr value)
487
488 # Subscripts are lists of expressions
489 # a[:i, n] (we don't have matrices, but we have data frames)
490 Subscript = (Token left, expr obj, expr index)
491
492 # Attributes are obj.attr, d->key, name::scope,
493 Attribute = (expr obj, Token op, Token attr, str attr_name, expr_context ctx)
494
495 y_lhs =
496 Var %Token # Id.Expr_Name
497 | Subscript %Subscript
498 | Attribute %Attribute
499
500 place_op =
501 # &a[i+1]
502 Subscript(Token op, expr index)
503 # &d.mykey
504 | Attribute(Token op, Token attr)
505
506 expr =
507 Var(Token left, str name) # a variable name to evaluate
508 # Constants are typically Null, Bool, Int, Float
509 # and also Str for key in {key: 42}
510 # But string literals are SingleQuoted or DoubleQuoted
511 # Python uses Num(object n), which doesn't respect our "LST" invariant.
512 | Const(Token c, value val)
513
514 # read(&x) json read (&x[0])
515 | Place(Token blame_tok, str var_name, place_op* ops)
516
517 # :| one 'two' "$three" |
518 | ShArrayLiteral %ShArrayLiteral
519
520 # / d+ ; ignorecase; %python /
521 | Eggex %Eggex
522
523 # $name is not an expr, but $? is, e.g. Id.VSub_QMark
524 | SimpleVarSub %SimpleVarSub
525 | BracedVarSub %BracedVarSub
526 | CommandSub %CommandSub
527 | SingleQuoted %SingleQuoted
528 | DoubleQuoted %DoubleQuoted
529
530 | Literal(expr inner)
531 | Lambda(List[NameType] params, expr body)
532
533 | Unary(Token op, expr child)
534 | Binary(Token op, expr left, expr right)
535 # x < 4 < 3 and (x < 4) < 3
536 | Compare(expr left, List[Token] ops, List[expr] comparators)
537 | FuncCall(expr func, ArgList args)
538
539 # TODO: Need a representation for method call. We don't just want
540 # Attribute() and then Call()
541
542 | IfExp(expr test, expr body, expr orelse)
543 | Tuple(Token left, List[expr] elts, expr_context ctx)
544
545 | List(Token left, List[expr] elts, expr_context ctx)
546 | Dict(Token left, List[expr] keys, List[expr] values)
547 # For the values in {n1, n2}
548 | Implicit
549
550 | ListComp(Token left, expr elt, List[Comprehension] generators)
551 # not implemented
552 | DictComp(Token left, expr key, expr value, List[Comprehension] generators)
553 | GeneratorExp(expr elt, List[Comprehension] generators)
554
555 # Ranges are written 1:2, with first class expression syntax. There is no
556 # step as in Python. Use range(0, 10, step=2) for that.
557 | Range(expr lower, Token op, expr upper)
558
559 # Slices occur within [] only. Unlike ranges, the start/end can be #
560 # implicit. Like ranges, denote a step with slice(0, 10, step=2).
561 # a[3:] a[:i]
562 | Slice(expr? lower, Token op, expr? upper)
563
564 | Subscript %Subscript
565 | Attribute %Attribute
566
567 # Ellipsis is like 'Starred' within Python, which are valid on the LHS in
568 # Python for unpacking, and # within list literals for splicing.
569 # (Starred is NOT used for {k:v, **a}. That used a blank "keys"
570 # attribute.)
571
572 # I think we can use { **pairs } like Python
573 | Spread(Token left, expr child)
574
575 #
576 # Regex Language (Eggex)
577 #
578
579 # e.g. alnum digit
580 PosixClass = (Token? negated, str name)
581 # e.g. d w s
582 PerlClass = (Token? negated, str name)
583
584 # Char Sets and Ranges both use Char Codes
585 # with u_braced == true : \u{ff}
586 # with u_braced == false: \xff \\ 'a' a '0' 0
587 # ERE doesn't make a distinction, but compiling to Python/PCRE can use it
588 CharCode = (Token blame_tok, int i, bool u_braced)
589 CharRange = (CharCode start, CharCode end)
590
591 # Note: .NET has && in character classes, making it a recursive language
592
593 class_literal_term =
594 PosixClass %PosixClass
595 | PerlClass %PerlClass
596 | CharRange %CharRange
597 | CharCode %CharCode
598
599 | SingleQuoted %SingleQuoted
600 # @chars
601 | Splice(Token name, str var_name) # coudl be Splice %Token
602
603 # evaluated version of class_literal_term (could be in runtime.asdl)
604 char_class_term =
605 PosixClass %PosixClass
606 | PerlClass %PerlClass
607
608 | CharRange %CharRange
609 # For [ \x00 \\ ]
610 | CharCode %CharCode
611
612 # NOTE: modifier is unused now, can represent L or P
613 re_repeat =
614 Op %Token # + * ? or Expr_DecInt for x{3}
615 | Range(Token? left, str lower, str upper, Token? right) # dot{1,2}
616 # Haven't implemented the modifier, e.g. x{+ P}
617 # | Num(Token times, id modifier)
618 # | Range(Token? lower, Token? upper, id modifier)
619
620 re =
621 Primitive(Token blame_tok, id id) # . ^ $ dot %start %end
622 | PosixClass %PosixClass
623 | PerlClass %PerlClass
624 # syntax [ $x \n ]
625 | CharClassLiteral(bool negated, List[class_literal_term] terms)
626 # evaluated [ 'abc' \n ]
627 | CharClass(bool negated, List[char_class_term] terms)
628
629 # @D
630 | Splice(Token name, str var_name) # TODO: Splice %Token ?
631
632 | SingleQuoted %SingleQuoted
633
634 # Compound:
635 | Repeat(re child, re_repeat op)
636 | Seq(List[re] children)
637 | Alt(List[re] children)
638
639 | Group(re child)
640 # convert_func is filled in on evaluation
641 # TODO: name and func_name can be expanded to strings
642 | Capture(re child, Token? name, Token? func_name)
643 | Backtracking(bool negated, Token name, re child)
644
645 # \u{ff} is parsed as this, but SingleQuoted also evaluates to it
646 | LiteralChars(Token blame_tok, str s)
647}