Actually just three programming languages in a trenchcoat
1use super::*;
2use crate::{Parser, Spanned};
3use source_span::Span;
4use trilogy_scanner::{
5 Token,
6 TokenType::{self, *},
7};
8
9/// An assignment statement. An assignment may have one of many "strategies".
10///
11/// [`FunctionAssignment`][] is parsed separately.
12///
13/// ```trilogy
14/// lhs = rhs
15/// ```
16#[derive(Clone, Debug, PrettyPrintSExpr)]
17pub struct AssignmentStatement {
18 pub lhs: Expression,
19 pub strategy: AssignmentStrategy,
20 pub rhs: Expression,
21 span: Span,
22}
23
24impl Spanned for AssignmentStatement {
25 fn span(&self) -> Span {
26 self.span
27 }
28}
29
30impl AssignmentStatement {
31 pub(crate) const ASSIGNMENT_OPERATOR: [TokenType; 24] = [
32 OpEq,
33 OpAmpAmpEq,
34 OpPipePipeEq,
35 OpAmpEq,
36 OpPipeEq,
37 OpCaretEq,
38 OpShrEq,
39 OpShlEq,
40 OpShrExEq,
41 OpShlExEq,
42 OpShrConEq,
43 OpShlConEq,
44 OpGlueEq,
45 OpPlusEq,
46 OpMinusEq,
47 OpStarEq,
48 OpSlashEq,
49 OpSlashSlashEq,
50 OpPercentEq,
51 OpStarStarEq,
52 OpLtLtEq,
53 OpGtGtEq,
54 OpColonEq,
55 OpDotEq,
56 ];
57
58 pub(crate) fn parse(parser: &mut Parser, lhs: Expression) -> SyntaxResult<Self> {
59 let strategy = AssignmentStrategy::parse(parser)?;
60 let rhs = Expression::parse(parser)?;
61 Ok(Self {
62 span: lhs.span().union(rhs.span()),
63 lhs,
64 strategy,
65 rhs,
66 })
67 }
68}
69
70/// The strategy of an assignment statement.
71#[derive(Clone, Debug, Spanned, PrettyPrintSExpr)]
72pub enum AssignmentStrategy {
73 Direct(Token),
74 And(Token),
75 Or(Token),
76 Add(Token),
77 Subtract(Token),
78 Multiply(Token),
79 Divide(Token),
80 Remainder(Token),
81 Power(Token),
82 IntDivide(Token),
83 BitwiseAnd(Token),
84 BitwiseOr(Token),
85 BitwiseXor(Token),
86 LeftShift(Token),
87 RightShift(Token),
88 LeftShiftExtend(Token),
89 RightShiftExtend(Token),
90 LeftShiftContract(Token),
91 RightShiftContract(Token),
92 Glue(Token),
93 Compose(Token),
94 RCompose(Token),
95 Access(Token),
96 Cons(Token),
97}
98
99impl AssignmentStrategy {
100 fn parse(parser: &mut Parser) -> SyntaxResult<Self> {
101 let token = parser
102 .expect(AssignmentStatement::ASSIGNMENT_OPERATOR)
103 .map_err(|token| {
104 parser.expected(token, "expected assignment operator (ending with `=`)")
105 })?;
106 Ok(match token.token_type {
107 OpEq => Self::Direct(token),
108 OpAmpAmpEq => Self::And(token),
109 OpPipePipeEq => Self::Or(token),
110 OpAmpEq => Self::BitwiseAnd(token),
111 OpPipeEq => Self::BitwiseOr(token),
112 OpCaretEq => Self::BitwiseXor(token),
113 OpShlEq => Self::LeftShift(token),
114 OpShrEq => Self::RightShift(token),
115 OpShlExEq => Self::LeftShiftExtend(token),
116 OpShrExEq => Self::RightShiftExtend(token),
117 OpShlConEq => Self::LeftShiftContract(token),
118 OpShrConEq => Self::RightShiftContract(token),
119 OpGlueEq => Self::Glue(token),
120 OpPlusEq => Self::Add(token),
121 OpMinusEq => Self::Subtract(token),
122 OpStarEq => Self::Multiply(token),
123 OpSlashEq => Self::Divide(token),
124 OpSlashSlashEq => Self::IntDivide(token),
125 OpPercentEq => Self::Remainder(token),
126 OpStarStarEq => Self::Power(token),
127 OpLtLtEq => Self::RCompose(token),
128 OpGtGtEq => Self::Compose(token),
129 OpColonEq => Self::Cons(token),
130 OpDotEq => Self::Access(token),
131 _ => unreachable!(),
132 })
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 test_parse!(assignment_direct: "x = 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
141 test_parse!(assignment_and: "x &&= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::And _) _))");
142 test_parse!(assignment_or: "x ||= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Or _) _))");
143 test_parse!(assignment_add: "x += 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Add _) _))");
144 test_parse!(assignment_subtract: "x -= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Subtract _) _))");
145 test_parse!(assignment_multiply: "x *= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Multiply _) _))");
146 test_parse!(assignment_divide: "x /= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Divide _) _))");
147 test_parse!(assignment_remainder: "x %= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Remainder _) _))");
148 test_parse!(assignment_power: "x **= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Power _) _))");
149 test_parse!(assignment_int_divide: "x //= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::IntDivide _) _))");
150 test_parse!(assignment_bitwise_and: "x &= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::BitwiseAnd _) _))");
151 test_parse!(assignment_bitwise_or: "x |= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::BitwiseOr _) _))");
152 test_parse!(assignment_bitwise_xor: "x ^= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::BitwiseXor _) _))");
153 test_parse!(assignment_left_shift: "x <~= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::LeftShift _) _))");
154 test_parse!(assignment_right_shift: "x ~>= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::RightShift _) _))");
155 test_parse!(assignment_left_shift_ex: "x <~~= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::LeftShiftExtend _) _))");
156 test_parse!(assignment_right_shift_ex: "x ~~>= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::RightShiftExtend _) _))");
157 test_parse!(assignment_left_shift_con: "x <<~= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::LeftShiftContract _) _))");
158 test_parse!(assignment_right_shift_con: "x ~>>= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::RightShiftContract _) _))");
159 test_parse!(assignment_glue: "x <>= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Glue _) _))");
160 test_parse!(assignment_compose: "x >>= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Compose _) _))");
161 test_parse!(assignment_rcompose: "x <<= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::RCompose _) _))");
162 test_parse!(assignment_access: "x .= 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Access _) _))");
163 test_parse!(assignment_cons: "x := 5" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Cons _) _))");
164
165 test_parse_error!(assignment_not_fn: "a x = 7" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
166 test_parse_error!(assignment_not_proc: "a!() = 7" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
167 test_parse_error!(assignment_contains_not: "[a, a!()] = 7" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
168
169 test_parse!(assignment_left_access: "a.b = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
170 test_parse!(assignment_proc_but_access: "a!().x = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
171 test_parse!(assignment_left_array: "[a, b, c] = [1, 2, 3]" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
172 test_parse!(assignment_left_array_spread_start: "[..a, b, c] = []" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
173 test_parse!(assignment_left_array_spread_middle: "[a, ..b, c] = []" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
174 test_parse!(assignment_left_array_spread_end: "[a, b, ..c] = []" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
175 test_parse_error!(assignment_left_array_spread_multi: "[..a, b, ..c] = []" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
176 test_parse!(assignment_left_record: "{| \"a\" => a |} = {||}" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
177 test_parse!(assignment_left_record_spread: "{| \"a\" => b, ..c |} = {||}" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
178 test_parse_error!(assignment_left_record_spread_not_last: "{| ..c, \"a\" => b |} = {||}" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
179 test_parse_error!(assignment_left_record_spread_multi: "{| ..a, ..c |} = {||}" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
180 test_parse!(assignment_left_set: "[| a, b |] = [||]" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
181 test_parse!(assignment_left_set_spread: "[| \"a\", ..c |] = [||]" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
182 test_parse_error!(assignment_left_set_spread_not_last: "[| ..c, \"a\" |] = [||]" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
183 test_parse_error!(assignment_left_set_spread_multi: "[| ..a, ..c |] = [||]" => Statement::parse => "cannot assign to an expression that is not a valid assignment target");
184 test_parse!(assignment_left_glue: "\"hello \" <> world = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
185 test_parse!(assignment_left_neg: "-world = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
186 test_parse!(assignment_left_cons: "hello : world = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
187 test_parse!(assignment_left_struct: "'hello(x) = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
188 test_parse!(assignment_left_paren: "(x) = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
189 test_parse!(assignment_left_lit_false: "false = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
190 test_parse!(assignment_left_lit_true: "true = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
191 test_parse!(assignment_left_lit_unit: "unit = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
192 test_parse!(assignment_left_lit_atom: "'atom = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
193 test_parse!(assignment_left_lit_num: "7 = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
194 test_parse!(assignment_left_lit_char: "'7' = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
195 test_parse!(assignment_left_lit_str: "\"7\" = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
196 test_parse!(assignment_left_lit_bits: "0bb0101 = 7" => Statement::parse => "(Statement::Assignment (AssignmentStatement _ (AssignmentStrategy::Direct _) _))");
197
198 test_parse_error!(assignment_left_block: "{ call!() } = 7" => Statement::parse);
199 test_parse_error!(assignment_left_block_empty: "{} = 7" => Statement::parse);
200 test_parse_error!(assignment_left_record_no_paren: "{ x => y } = 7" => Statement::parse);
201}