summaryrefslogtreecommitdiffstats
path: root/src/shell.pest
blob: 92b173afc5c58a540bfc769d29f8fbbe73b1fea5 (plain) (blame)
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
basic_escape_char = @{ "\\\\" | "\\'" }
escape_char       = @{ "\\" ~ ANY }

bareword_char = @{
    escape_char |
    !("|" | ";" | "\"" | "'" | "$" | "{" | "(" | ")" | WHITESPACE | COMMENT)
        ~ ANY
}
single_string_char = @{ basic_escape_char | (!"'" ~ ANY) }
double_string_char = @{ escape_char | (!("\"" | "$") ~ ANY) }

var = @{
    ("$" ~ XID_START ~ XID_CONTINUE*) |
    ("$" ~ ("?" | "$" | "*" | ASCII_DIGIT)) |
    ("${" ~ (!"}" ~ ANY)+ ~ "}")
}
bareword      = @{ bareword_char+ }
single_string = @{ single_string_char+ }
double_string = @{ double_string_char+ }

alternation_bareword_char = @{ !("," | "}") ~ bareword_char }
alternation_bareword = @{ alternation_bareword_char+ }
alternation_word_part = ${
    var |
    alternation_bareword |
    "'" ~ single_string? ~ "'" |
    "\"" ~ (var | double_string)* ~ "\""
}
alternation_word = ${ alternation_word_part* }
alternation = ${ "{" ~ alternation_word ~ ("," ~ alternation_word)* ~ "}" }

substitution = ${ "$(" ~ w? ~ commands ~ w? ~ ")"}

word_part = ${
    alternation |
    substitution |
    var |
    bareword |
    "'" ~ single_string? ~ "'" |
    "\"" ~ (substitution | var | double_string)* ~ "\""
}
word = ${ word_part+ }

redir_prefix = @{
    ("in" | "out" | "err" | ASCII_DIGIT*) ~ (">>" | ">" | "<")
}
redirect = ${ redir_prefix ~ w? ~ word }

exe      = ${ (redirect | word) ~ (w ~ (redirect | word))* }
subshell = ${
    "(" ~ w? ~ commands ~ w? ~ ")" ~ (w? ~ redirect ~ (w ~ redirect)*)?
}
list     = ${ word ~ (w ~ word)* }
pipeline = ${ (subshell | exe) ~ (w? ~ "|" ~ w? ~ (subshell | exe))* }

control_if    = ${ "if" ~ w ~ pipeline }
control_while = ${ "while" ~ w ~ pipeline }
control_for   = ${ "for" ~ w ~ bareword ~ w ~ "in" ~ w ~ list }
control_else  = ${ "else" ~ (w ~ "if" ~ w ~ pipeline)? }
control_end   = ${ "end" }
control       = ${
    control_if | control_while | control_for | control_else | control_end
}

command  = ${ control | pipeline }
commands = ${ command ~ (w? ~ ";" ~ w? ~ command)* }

line = ${ SOI ~ w? ~ commands ~ w? ~ EOI }

w          = _{ (WHITESPACE | COMMENT)+ }
WHITESPACE = _{ (" " | "\t" | "\n") }
COMMENT    = _{ "#" ~ ANY* }