-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpr.lark
More file actions
81 lines (67 loc) · 3.03 KB
/
Copy pathexpr.lark
File metadata and controls
81 lines (67 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
%import common.INT -> INT
// this matches double-quoted strings: e.g., "hello world"
%import common.ESCAPED_STRING -> ESCAPED_STRING
%import common.CNAME -> ID
%import common.WS
// Custom terminal for matching anything between backticks: e.g., `echo hello world`
BACKTICK_CMD: /`[^`]+`/
// This tells the lexer to discard whitespace
%ignore WS
?expr: assign_level ";" expr -> seq
| assign_level
?assign_level: "if" expr "then" expr "else" assign_level -> ite
| ID ":=" assign_level -> assign
| "show" assign_level -> show
| pipe_level
// Shell pipe (left-associative, sits just above if-then-else).
// Pipe connects two whole process expressions, so it binds more loosely
// than redirections — this ensures redirections are applied to individual
// commands before those commands are piped together.
// e.g. `ls` > "out.txt" | `grep py` correctly parses as
// Pipe(RedirectOut(`ls`, "out.txt"), `grep py`)
?pipe_level: pipe_level "|" or_level -> pipe
| or_level
// Boolean or (left-associative)
?or_level: or_level "||" and_level -> or
| and_level
// Boolean and (left-associative)
?and_level: and_level "&&" not_level -> and
| not_level
// Boolean not (unary, right-recursive)
?not_level: "!" not_level -> not
| compare_level
// Equality and relational (non-associative)
?compare_level: redir_level "==" redir_level -> eq
| redir_level "<" redir_level -> lt
| redir_level
// Shell redirections (left-associative, sits between +/- and comparisons).
// Redirections modify a single command — they're an attribute of a process —
// so they bind tightly, close to the command itself. Placing them above
// comparisons means e.g. `ls` > "out.txt" == `cat` > "out.txt" correctly
// parses as Eq(RedirectOut(`ls`,...), RedirectOut(`cat`,...)).
// RHS is always an ESCAPED_STRING, so "<" here never clashes with Lt above.
?redir_level: redir_level "<" ESCAPED_STRING -> redirect_in
| redir_level ">" ESCAPED_STRING -> redirect_out
| redir_level "!>" ESCAPED_STRING -> redirect_err
| redir_level ">>" ESCAPED_STRING -> redirect_append
| redir_level "tee" ESCAPED_STRING -> tee
| add_level
// Addition / subtraction (left-associative)
?add_level: add_level "+" mul_level -> add
| add_level "-" mul_level -> sub
| mul_level
// Multiplication / division (left-associative)
?mul_level: mul_level "*" neg_level -> mul
| mul_level "/" neg_level -> div
| neg_level
// Unary negation (right-recursive, binds tighter than * /)
?neg_level: "-" neg_level -> neg
| atom
// Highest precedence: atoms
?atom: ID -> id
| INT -> int
| BACKTICK_CMD -> cmd
| atom "(" expr ")" -> app
| "(" expr ")"
| "let" ID "=" expr "in" expr "end" -> let
| "letfun" ID "(" ID ")" "=" expr "in" expr "end" -> letfun