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)]
17pub struct AssignmentStatement {
18 pub lhs: Expression,
19 pub strategy: AssignmentStrategy,
20 pub rhs: Expression,
21 pub 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)]
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 { strategy: AssignmentStrategy::Direct(_), .. }));
141 test_parse!(assignment_and: "x &&= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::And(_), .. }));
142 test_parse!(assignment_or: "x ||= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Or(_), .. }));
143 test_parse!(assignment_add: "x += 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Add(_), .. }));
144 test_parse!(assignment_subtract: "x -= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Subtract(_), .. }));
145 test_parse!(assignment_multiply: "x *= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Multiply(_), .. }));
146 test_parse!(assignment_divide: "x /= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Divide(_), .. }));
147 test_parse!(assignment_remainder: "x %= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Remainder(_), .. }));
148 test_parse!(assignment_power: "x **= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Power(_), .. }));
149 test_parse!(assignment_int_divide: "x //= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::IntDivide(_), .. }));
150 test_parse!(assignment_bitwise_and: "x &= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::BitwiseAnd(_), .. }));
151 test_parse!(assignment_bitwise_or: "x |= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::BitwiseOr(_), .. }));
152 test_parse!(assignment_bitwise_xor: "x ^= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::BitwiseXor(_), .. }));
153 test_parse!(assignment_left_shift: "x <~= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::LeftShift(_), .. }));
154 test_parse!(assignment_right_shift: "x ~>= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::RightShift(_), .. }));
155 test_parse!(assignment_left_shift_ex: "x <~~= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::LeftShiftExtend(_), .. }));
156 test_parse!(assignment_right_shift_ex: "x ~~>= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::RightShiftExtend(_), .. }));
157 test_parse!(assignment_left_shift_con: "x <<~= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::LeftShiftContract(_), .. }));
158 test_parse!(assignment_right_shift_con: "x ~>>= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::RightShiftContract(_), .. }));
159 test_parse!(assignment_glue: "x <>= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Glue(_), .. }));
160 test_parse!(assignment_compose: "x >>= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Compose(_), .. }));
161 test_parse!(assignment_rcompose: "x <<= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::RCompose(_), .. }));
162 test_parse!(assignment_access: "x .= 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Access(_), .. }));
163 test_parse!(assignment_cons: "x := 5" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: 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 { strategy: AssignmentStrategy::Direct(_), .. }));
170 test_parse!(assignment_proc_but_access: "a!().x = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
171 test_parse!(assignment_left_array: "[a, b, c] = [1, 2, 3]" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
172 test_parse!(assignment_left_array_spread_start: "[..a, b, c] = []" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
173 test_parse!(assignment_left_array_spread_middle: "[a, ..b, c] = []" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
174 test_parse!(assignment_left_array_spread_end: "[a, b, ..c] = []" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: 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 { strategy: AssignmentStrategy::Direct(_), .. }));
177 test_parse!(assignment_left_record_spread: "{| \"a\" => b, ..c |} = {||}" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: 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 { strategy: AssignmentStrategy::Direct(_), .. }));
181 test_parse!(assignment_left_set_spread: "[| \"a\", ..c |] = [||]" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: 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 { strategy: AssignmentStrategy::Direct(_), .. }));
185 test_parse!(assignment_left_neg: "-world = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
186 test_parse!(assignment_left_cons: "hello : world = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
187 test_parse!(assignment_left_struct: "'hello(x) = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
188 test_parse!(assignment_left_paren: "(x) = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
189 test_parse!(assignment_left_lit_false: "false = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
190 test_parse!(assignment_left_lit_true: "true = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
191 test_parse!(assignment_left_lit_unit: "unit = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
192 test_parse!(assignment_left_lit_atom: "'atom = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
193 test_parse!(assignment_left_lit_num: "7 = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
194 test_parse!(assignment_left_lit_char: "'7' = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
195 test_parse!(assignment_left_lit_str: "\"7\" = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: AssignmentStrategy::Direct(_), .. }));
196 test_parse!(assignment_left_lit_bits: "0bb0101 = 7" => Statement::parse => Statement::Assignment(AssignmentStatement { strategy: 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}