blanket_script/parser/
expression.rs

1use nom::{
2    branch::alt,
3    bytes::complete::tag,
4    character::complete::{none_of, one_of},
5    combinator::{cut, map, opt},
6    multi::{count, many0, many1, many_m_n},
7    sequence::{delimited, pair, preceded, terminated},
8    IResult, Parser,
9};
10use std::fmt;
11
12use super::{ws0, ParseResult, SyntaxError, SyntaxErrorKind};
13
14#[derive(Debug, PartialEq, Clone)]
15pub enum Expression {
16    Number(String),
17    BigInt(String),
18    String(StringLiteral),
19    UnaryOperation {
20        operator: UnaryOperator,
21        operand: Box<Expression>,
22    },
23    BinaryOperation {
24        operator: BinaryOperator,
25        left: Box<Expression>,
26        right: Box<Expression>,
27    },
28}
29
30impl fmt::Display for Expression {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match self {
33            Self::Number(number) => write!(f, "{}", number),
34            Self::BigInt(bigint) => write!(f, "{}n", bigint),
35            Self::String(string) => write!(f, "\"{}\"", string.value),
36            Self::UnaryOperation { operator, operand } => match operator {
37                UnaryOperator::Try => write!(f, "({}?)", operand),
38                _ => write!(f, "({}{})", operator, operand),
39            },
40            Self::BinaryOperation {
41                operator,
42                left,
43                right,
44            } => write!(f, "({} {} {})", left, operator, right),
45        }
46    }
47}
48
49/// String literals can contain interpolations, which are expressions that are
50/// evaluated and inserted into the string at the location of the interpolation.
51/// For example, the string `"Hello, {name}!"` contains a single interpolation
52/// at index 7.
53#[derive(Debug, PartialEq, Clone)]
54pub struct StringLiteral {
55    /// The string value with interpolations removed.
56    pub value: String,
57    /// The list of interpolations and their indices in the string.
58    pub interpolations: Vec<(usize, Box<Expression>)>,
59}
60
61#[derive(Debug, PartialEq, Clone)]
62enum StringPart {
63    Literal(String),
64    Interpolation(Box<Expression>),
65}
66
67#[derive(Debug, Eq, PartialEq, Clone, Copy)]
68pub enum UnaryOperator {
69    Negate,
70    LogicalNot,
71    BitwiseNot,
72    UnaryPlus,
73    UnaryMinus,
74    Try,
75}
76
77impl fmt::Display for UnaryOperator {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        match self {
80            Self::Negate => write!(f, "!"),
81            Self::LogicalNot => write!(f, "!"),
82            Self::BitwiseNot => write!(f, "~"),
83            Self::UnaryPlus => write!(f, "+"),
84            Self::UnaryMinus => write!(f, "-"),
85            Self::Try => write!(f, "?"),
86        }
87    }
88}
89
90impl std::str::FromStr for UnaryOperator {
91    type Err = ();
92
93    fn from_str(s: &str) -> Result<Self, Self::Err> {
94        match s {
95            "!" => Ok(Self::Negate),
96            "~" => Ok(Self::BitwiseNot),
97            "+" => Ok(Self::UnaryPlus),
98            "-" => Ok(Self::UnaryMinus),
99            "?" => Ok(Self::Try),
100            _ => Err(()),
101        }
102    }
103}
104
105#[derive(Debug, Eq, PartialEq, Clone, Copy)]
106pub enum Associativity {
107    LeftToRight,
108    RightToLeft,
109}
110
111#[derive(Debug, Eq, PartialEq, Clone, Copy)]
112pub enum BinaryOperator {
113    /// Exponentiation operator `**`
114    Exponent,
115    /// Multiplication operator `*`
116    Multiply,
117    /// Division operator `/`
118    Divide,
119    /// Modulus operator `%`
120    Modulo,
121    /// Add operator `+`
122    Add,
123    /// Subtract operator `-`
124    Subtract,
125    /// Left shift operator `<<`
126    LeftShift,
127    /// Right shift operator `>>`
128    RightShift,
129    /// Less than operator `<`
130    LessThan,
131    /// Less than or equal operator `<=`
132    LessThanOrEqual,
133    /// Greater than operator `>`
134    GreaterThan,
135    /// Greater than or equal operator `>=`
136    GreaterThanOrEqual,
137    /// Equality operator `==`
138    Equal,
139    /// Not equal operator `!=`
140    NotEqual,
141    /// Bitwise AND operator `&`
142    BitwiseAnd,
143    /// Bitwise XOR operator `^`
144    BitwiseXor,
145    /// Bitwise OR operator `|`
146    BitwiseOr,
147    /// Logical AND operator `&&`
148    LogicalAnd,
149    /// Logical OR operator `||`
150    LogicalOr,
151    /// Pipe operator `|>`
152    Pipe,
153}
154
155impl BinaryOperator {
156    pub fn precedence(&self) -> u8 {
157        match self {
158            Self::Exponent => 13,
159            Self::Multiply | Self::Divide | Self::Modulo => 12,
160            Self::Add | Self::Subtract => 11,
161            Self::LeftShift | Self::RightShift => 10,
162            Self::LessThan
163            | Self::LessThanOrEqual
164            | Self::GreaterThan
165            | Self::GreaterThanOrEqual => 9,
166            Self::Equal | Self::NotEqual => 8,
167            Self::BitwiseAnd => 7,
168            Self::BitwiseXor => 6,
169            Self::BitwiseOr => 5,
170            Self::LogicalAnd => 4,
171            Self::LogicalOr => 3,
172            Self::Pipe => 2,
173        }
174    }
175
176    pub fn associativity(&self) -> Associativity {
177        match self {
178            Self::Exponent => Associativity::RightToLeft,
179            Self::Multiply
180            | Self::Divide
181            | Self::Modulo
182            | Self::Add
183            | Self::Subtract
184            | Self::LeftShift
185            | Self::RightShift
186            | Self::LessThan
187            | Self::LessThanOrEqual
188            | Self::GreaterThan
189            | Self::GreaterThanOrEqual
190            | Self::Equal
191            | Self::NotEqual
192            | Self::BitwiseAnd
193            | Self::BitwiseXor
194            | Self::BitwiseOr
195            | Self::LogicalAnd
196            | Self::LogicalOr
197            | Self::Pipe => Associativity::LeftToRight,
198        }
199    }
200}
201
202impl std::str::FromStr for BinaryOperator {
203    type Err = ();
204
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        match s {
207            "**" => Ok(Self::Exponent),
208            "*" => Ok(Self::Multiply),
209            "/" => Ok(Self::Divide),
210            "%" => Ok(Self::Modulo),
211            "+" => Ok(Self::Add),
212            "-" => Ok(Self::Subtract),
213            "<<" => Ok(Self::LeftShift),
214            ">>" => Ok(Self::RightShift),
215            "<" => Ok(Self::LessThan),
216            "<=" => Ok(Self::LessThanOrEqual),
217            ">" => Ok(Self::GreaterThan),
218            ">=" => Ok(Self::GreaterThanOrEqual),
219            "==" => Ok(Self::Equal),
220            "!=" => Ok(Self::NotEqual),
221            "&" => Ok(Self::BitwiseAnd),
222            "^" => Ok(Self::BitwiseXor),
223            "|" => Ok(Self::BitwiseOr),
224            "&&" => Ok(Self::LogicalAnd),
225            "||" => Ok(Self::LogicalOr),
226            "|>" => Ok(Self::Pipe),
227            _ => Err(()),
228        }
229    }
230}
231
232impl fmt::Display for BinaryOperator {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        match self {
235            Self::Exponent => write!(f, "**"),
236            Self::Multiply => write!(f, "*"),
237            Self::Divide => write!(f, "/"),
238            Self::Modulo => write!(f, "%"),
239            Self::Add => write!(f, "+"),
240            Self::Subtract => write!(f, "-"),
241            Self::LeftShift => write!(f, "<<"),
242            Self::RightShift => write!(f, ">>"),
243            Self::LessThan => write!(f, "<"),
244            Self::LessThanOrEqual => write!(f, "<="),
245            Self::GreaterThan => write!(f, ">"),
246            Self::GreaterThanOrEqual => write!(f, ">="),
247            Self::Equal => write!(f, "=="),
248            Self::NotEqual => write!(f, "!="),
249            Self::BitwiseAnd => write!(f, "&"),
250            Self::BitwiseXor => write!(f, "^"),
251            Self::BitwiseOr => write!(f, "|"),
252            Self::LogicalAnd => write!(f, "&&"),
253            Self::LogicalOr => write!(f, "||"),
254            Self::Pipe => write!(f, "|>"),
255        }
256    }
257}
258
259fn non_zero_digit(input: &str) -> ParseResult<char> {
260    one_of("123456789")(input)
261}
262
263fn digit(input: &str) -> ParseResult<char> {
264    one_of("0123456789")(input)
265}
266
267fn hex_digit(input: &str) -> ParseResult<char> {
268    let result: IResult<&str, char> = one_of("0123456789abcdefABCDEF").parse(input);
269    match result {
270        Ok((input, c)) => Ok((input, c)),
271        Err(_) => Err(nom::Err::Error(SyntaxError {
272            input,
273            error: SyntaxErrorKind::InvalidHexDigit,
274        })),
275    }
276}
277
278fn delimited_digit(input: &str) -> ParseResult<char> {
279    if input.starts_with("__") {
280        return Err(nom::Err::Failure(SyntaxError {
281            input,
282            error: SyntaxErrorKind::ConsecutiveUnderscoreInNumericLiteral,
283        }));
284    }
285    alt((digit, preceded(tag("_"), digit))).parse(input)
286}
287
288fn whole_number(input: &str) -> ParseResult<String> {
289    if input.starts_with('_') {
290        return Err(nom::Err::Failure(SyntaxError {
291            input,
292            error: SyntaxErrorKind::NumericLiteralStartsWithUnderscore,
293        }));
294    }
295    // Must start with a non-zero digit
296    let (input, start) = non_zero_digit(input)?;
297    // Followed by zero or more digits
298    // Underscores are allowed between digits but not at the start or end
299    let (input, digits) = many0(delimited_digit).parse(input)?;
300    // Concatenate the digits into a single string
301    let number = std::iter::once(start).chain(digits).collect::<String>();
302    Ok((input, number))
303}
304
305fn decimal_number(input: &str) -> ParseResult<String> {
306    let (input, mut number) = whole_number(input)?;
307    // Optionally followed by a decimal point and fractional part
308    let (input, decimal) = opt(preceded(tag("."), many1(delimited_digit))).parse(input)?;
309    if input.starts_with('_') {
310        return Err(nom::Err::Failure(SyntaxError {
311            input,
312            error: SyntaxErrorKind::NumericLiteralEndsWithUnderscore,
313        }));
314    }
315    // Add the decimal point and fractional part if present
316    if let Some(decimal) = decimal {
317        number.extend(std::iter::once('.').chain(decimal));
318    }
319    Ok((input, number))
320}
321
322fn scientific_notation(input: &str) -> ParseResult<String> {
323    let (input, mut number) = decimal_number(input)?;
324    let (input, sign) = preceded(alt((tag("e"), tag("E"))), opt(one_of("+-"))).parse(input)?;
325    let (input, exponent) = many1(delimited_digit).parse(input)?;
326    if input.starts_with('_') {
327        return Err(nom::Err::Failure(SyntaxError {
328            input,
329            error: SyntaxErrorKind::NumericLiteralEndsWithUnderscore,
330        }));
331    }
332    match sign {
333        Some('-') => number.push_str("e-"),
334        Some('+') => number.push('e'),
335        None => number.push('e'),
336        _ => unreachable!(),
337    };
338    number.extend(exponent);
339
340    Ok((input, number))
341}
342
343fn parse_number(input: &str) -> ParseResult<Expression> {
344    map(alt((scientific_notation, decimal_number)), |number| {
345        Expression::Number(number)
346    })
347    .parse(input)
348}
349
350fn parse_bigint(input: &str) -> ParseResult<Expression> {
351    if input.starts_with('n') {
352        return Err(nom::Err::Failure(SyntaxError {
353            input,
354            error: SyntaxErrorKind::EmptyBigIntLiteral,
355        }));
356    }
357    map(
358        terminated(alt((scientific_notation, whole_number)), tag("n")),
359        Expression::BigInt,
360    )
361    .parse(input)
362}
363
364fn parse_escaped_char(input: &str) -> ParseResult<char> {
365    let (input, result) = alt((
366        tag("\\\\"), // Double backslash
367        tag("\\\""), // Double quote
368        tag("\\n"),  // Newline
369        tag("\\r"),  // Carriage return
370        tag("\\t"),  // Tab
371        tag("\\0"),  // Null character
372        tag("{{"),   // Open curly brace
373        tag("}}"),   // Close curly brace
374    ))
375    .parse(input)?;
376    match result {
377        "\\\\" => Ok((input, '\\')),
378        "\\\"" => Ok((input, '"')),
379        "\\n" => Ok((input, '\n')),
380        "\\r" => Ok((input, '\r')),
381        "\\t" => Ok((input, '\t')),
382        "\\0" => Ok((input, '\0')),
383        "{{" => Ok((input, '{')),
384        "}}" => Ok((input, '}')),
385        _ => unreachable!(),
386    }
387}
388
389/// Parses a Unicode code point escape sequence which can be either `\xHHHH` or `\u{HHHH}`.
390/// The code point is converted to a `char` if it is valid or `SyntaxErrorKind::InvalidUnicodeCodePoint`
391/// is returned if the code point is not a valid Unicode code point.
392///
393/// ## Grammar
394///
395/// ```text
396/// EscapedCodePoint ::=
397///     | '\u' '{' HexDigit{2,6} '}'
398///     | '\x' HexDigit HexDigit
399/// ```
400fn parse_escaped_code_point(input: &str) -> ParseResult<char> {
401    let (input, value) = alt((
402        preceded(
403            tag("\\u"),
404            cut(delimited(tag("{"), many_m_n(2, 6, hex_digit), tag("}"))),
405        ),
406        preceded(tag("\\x"), cut(count(hex_digit, 2))),
407    ))
408    .parse(input)?;
409    let value = value.iter().collect::<String>();
410    let code_point = u32::from_str_radix(&value, 16).unwrap();
411    match std::char::from_u32(code_point) {
412        Some(c) => Ok((input, c)),
413        None => Err(nom::Err::Failure(SyntaxError {
414            input,
415            error: SyntaxErrorKind::InvalidUnicodeCodePoint(code_point),
416        })),
417    }
418}
419
420fn parse_regular_char(input: &str) -> ParseResult<char> {
421    none_of("\\\"{}").parse(input)
422}
423
424fn parse_interpolation(input: &str) -> ParseResult<StringPart> {
425    map(
426        delimited(tag("{"), alt((parse_number, parse_bigint)), tag("}")),
427        |inter| StringPart::Interpolation(inter.into()),
428    )
429    .parse(input)
430}
431
432fn parse_string(input: &str) -> ParseResult<Expression> {
433    let (input, parsed) = delimited(
434        tag("\""),
435        many0(alt((
436            map(
437                many1(alt((
438                    parse_regular_char,
439                    parse_escaped_char,
440                    parse_escaped_code_point,
441                ))),
442                |f| StringPart::Literal(f.iter().collect()),
443            ),
444            parse_interpolation,
445        ))),
446        tag("\""),
447    )
448    .parse(input)?;
449    let mut string = StringLiteral {
450        value: String::new(),
451        interpolations: vec![],
452    };
453    for part in parsed {
454        match part {
455            StringPart::Literal(literal) => string.value.push_str(&literal),
456            StringPart::Interpolation(interpolation) => {
457                string
458                    .interpolations
459                    .push((string.value.len(), interpolation));
460            }
461        }
462    }
463    Ok((input, Expression::String(string)))
464}
465
466fn parse_binary_operator(input: &str) -> ParseResult<BinaryOperator> {
467    map(
468        ws0(alt((
469            tag("|>"),
470            tag("**"),
471            tag("*"),
472            tag("/"),
473            tag("%"),
474            tag("+"),
475            tag("-"),
476            tag("<<"),
477            tag(">>"),
478            tag("<="),
479            tag("<"),
480            tag(">="),
481            tag(">"),
482            tag("=="),
483            tag("!="),
484            tag("&&"),
485            tag("||"),
486            tag("&"),
487            tag("^"),
488            tag("|"),
489        ))),
490        |op| op.parse().unwrap(),
491    )
492    .parse(input)
493}
494
495fn parse_unary_operator(input: &str) -> ParseResult<UnaryOperator> {
496    map(ws0(alt((tag("!"), tag("~"), tag("+"), tag("-")))), |op| {
497        op.parse().unwrap()
498    })
499    .parse(input)
500}
501
502fn parse_term(input: &str) -> ParseResult<Expression> {
503    map(
504        (
505            opt(parse_unary_operator),
506            alt((parse_string, parse_bigint, parse_number)),
507            opt(tag("?")),
508        ),
509        |(prefix, expr, postfix)| {
510            let mut expression = expr;
511            if postfix.is_some() {
512                expression = Expression::UnaryOperation {
513                    operator: UnaryOperator::Try,
514                    operand: Box::new(expression),
515                };
516            }
517            if let Some(op) = prefix {
518                expression = Expression::UnaryOperation {
519                    operator: op,
520                    operand: Box::new(expression),
521                };
522            }
523            expression
524        },
525    )
526    .parse(input)
527}
528
529/// Shunting-yard algorithm to parse an expression with binary operators.
530/// The algorithm uses two stacks: one for the output and one for operators.
531/// The output stack is used to store the operands and intermediate results
532/// while the operator stack is used to store the operators.
533///
534/// The algorithm works as follows:
535/// 1. Parse the left-hand side of the expression as a term.
536/// 2. While there are more tokens to read, (`opt` is used to handle the case)
537///     1. Read the next operator and right-hand side term.
538///     2. While there is an operator at the top of the operator stack with
539///        higher precedence or the same precedence with left-to-right
540///        associativity, pop the operator and apply it to the top two elements
541///        on the output stack.
542///     3. Push the operator onto the operator stack.
543///     4. Push the right-hand side term onto the output stack.
544/// 3. While there are operators on the operator stack, pop the operator and
545///    apply it to the top two elements on the output stack.
546/// 4. The final result is the last element on the output stack.
547/// 5. Return the result.
548pub(super) fn parse_expression(input: &str) -> ParseResult<Expression> {
549    let (input, left) = parse_term(input)?;
550    let mut output: Vec<Expression> = vec![left];
551    let mut operators = Vec::<BinaryOperator>::new();
552    let mut remaining = input;
553    loop {
554        let (input, opt_right) =
555            opt(pair(ws0(parse_binary_operator), ws0(parse_term))).parse(remaining)?;
556        remaining = input;
557        let (op, right) = match opt_right {
558            // Found an operator and a right-hand side term
559            Some((op, right)) => (op, right),
560            // No more operators, so we're done
561            None => break,
562        };
563        while let Some(&top) = operators.last() {
564            // If the operator at the top of the stack has higher precedence
565            // or the same precedence with left-to-right associativity, pop
566            // the operator and apply it to the top two elements on the output
567            // stack.
568            if top.precedence() > op.precedence()
569                || (top.precedence() == op.precedence()
570                    && op.associativity() == Associativity::LeftToRight)
571            {
572                let right = output.pop().unwrap();
573                let left = output.pop().unwrap();
574                output.push(Expression::BinaryOperation {
575                    operator: operators.pop().unwrap(),
576                    left: Box::new(left),
577                    right: Box::new(right),
578                });
579            } else {
580                break;
581            }
582        }
583        output.push(right);
584        operators.push(op);
585    }
586    while let Some(op) = operators.pop() {
587        let right = output.pop().unwrap();
588        let left = output.pop().unwrap();
589        output.push(Expression::BinaryOperation {
590            operator: op,
591            left: Box::new(left),
592            right: Box::new(right),
593        });
594    }
595    Ok((remaining, output.pop().unwrap()))
596}
597
598#[cfg(test)]
599mod tests {
600    use super::*;
601
602    macro_rules! test_expression {
603        ($name:ident, $input:expr, $expected:expr) => {
604            #[test]
605            fn $name() {
606                let (rest, result) = parse_expression($input).unwrap();
607                assert_eq!(rest, "");
608                assert_eq!(result, $expected);
609            }
610        };
611    }
612
613    macro_rules! test_error_kind {
614        ($name:ident, $input:expr, $kind:pat) => {
615            #[test]
616            fn $name() {
617                let result = parse_expression($input);
618                assert!(result.is_err());
619                let err = result.unwrap_err();
620                match err {
621                    nom::Err::Failure(SyntaxError { error, .. }) => assert!(matches!(error, $kind)),
622                    _ => panic!("Unexpected error: {:?}", err),
623                }
624            }
625        };
626    }
627
628    test_expression!(number_simple, "42", Expression::Number("42".into()));
629    test_expression!(number_pi, "3.14159", Expression::Number("3.14159".into()));
630    test_expression!(
631        number_pi_underscore,
632        "3.141_59",
633        Expression::Number("3.14159".into())
634    );
635    test_expression!(
636        number_billion,
637        "1000000000",
638        Expression::Number("1000000000".into())
639    );
640    test_expression!(
641        number_billion_with_underline,
642        "1_000_000_000",
643        Expression::Number("1000000000".into())
644    );
645    test_error_kind!(number_empty, "", SyntaxErrorKind::EmptyInput);
646    test_error_kind!(
647        number_underscore_at_start,
648        "_42",
649        SyntaxErrorKind::NumericLiteralStartsWithUnderscore
650    );
651    test_error_kind!(
652        number_underscore_at_end,
653        "42_",
654        SyntaxErrorKind::NumericLiteralEndsWithUnderscore
655    );
656    test_error_kind!(
657        number_decimal_underscore_at_end,
658        "42.0_",
659        SyntaxErrorKind::NumericLiteralEndsWithUnderscore
660    );
661    test_error_kind!(
662        number_exponent_underscore_at_end,
663        "42e10_",
664        SyntaxErrorKind::NumericLiteralEndsWithUnderscore
665    );
666    test_error_kind!(
667        number_consecutive_underscores,
668        "1__000",
669        SyntaxErrorKind::ConsecutiveUnderscoreInNumericLiteral
670    );
671    test_expression!(
672        number_scientific_notation,
673        "1e6",
674        Expression::Number("1e6".into())
675    );
676    test_expression!(
677        number_scientific_notation_uppercase,
678        "1E6",
679        Expression::Number("1e6".into())
680    );
681    test_expression!(
682        number_scientific_notation_positive,
683        "1e+6",
684        Expression::Number("1e6".into())
685    );
686    test_expression!(
687        number_scientific_notation_negative,
688        "1e-6",
689        Expression::Number("1e-6".into())
690    );
691    test_expression!(
692        number_scientific_notation_decimal,
693        "1.23e4",
694        Expression::Number("1.23e4".into())
695    );
696    test_expression!(
697        number_scientific_notation_decimal_positive,
698        "1.23e+4",
699        Expression::Number("1.23e4".into())
700    );
701    test_expression!(
702        number_scientific_notation_decimal_negative,
703        "1.23e-4",
704        Expression::Number("1.23e-4".into())
705    );
706
707    test_expression!(bigint_simple, "42n", Expression::BigInt("42".into()));
708    test_expression!(
709        bigint_billion,
710        "1000000000n",
711        Expression::BigInt("1000000000".into())
712    );
713    test_expression!(
714        bigint_billion_with_underline,
715        "1_000_000_000n",
716        Expression::BigInt("1000000000".into())
717    );
718    test_expression!(
719        bigint_scientific_notation,
720        "1e6n",
721        Expression::BigInt("1e6".into())
722    );
723    test_expression!(
724        bigint_scientific_notation_uppercase,
725        "1E6n",
726        Expression::BigInt("1e6".into())
727    );
728    test_expression!(
729        bigint_scientific_notation_positive,
730        "1e+6n",
731        Expression::BigInt("1e6".into())
732    );
733    test_expression!(
734        bigint_scientific_notation_negative,
735        "100e-2n",
736        Expression::BigInt("100e-2".into())
737    );
738    test_expression!(
739        bigint_scientific_notation_decimal,
740        "1.23e4n",
741        Expression::BigInt("1.23e4".into())
742    );
743    test_error_kind!(bigint_empty, "n", SyntaxErrorKind::EmptyBigIntLiteral);
744    test_error_kind!(
745        bigint_underscore_at_start,
746        "_42n",
747        SyntaxErrorKind::NumericLiteralStartsWithUnderscore
748    );
749    test_error_kind!(
750        bigint_underscore_at_end,
751        "42_n",
752        SyntaxErrorKind::NumericLiteralEndsWithUnderscore
753    );
754    test_error_kind!(
755        bigint_consecutive_underscores,
756        "1__000n",
757        SyntaxErrorKind::ConsecutiveUnderscoreInNumericLiteral
758    );
759
760    test_expression!(
761        string_empty,
762        r#""""#,
763        Expression::String(StringLiteral {
764            value: "".into(),
765            interpolations: vec![]
766        })
767    );
768    test_expression!(
769        string_simple,
770        r#""Hello, World!""#,
771        Expression::String(StringLiteral {
772            value: "Hello, World!".into(),
773            interpolations: vec![]
774        })
775    );
776    test_expression!(
777        string_escaped_chars,
778        r#""\\\"\n\r\t{{}}""#,
779        Expression::String(StringLiteral {
780            value: "\\\"\n\r\t{}".into(),
781            interpolations: vec![]
782        })
783    );
784    test_expression!(
785        string_unicode_code,
786        r#""\x41\u{41}\u{0041}\u{000041}""#,
787        Expression::String(StringLiteral {
788            value: "AAAA".into(),
789            interpolations: vec![]
790        })
791    );
792    test_expression!(
793        string_unicode_code_emoji,
794        r#""\u{2603}\u{1F600}""#,
795        Expression::String(StringLiteral {
796            value: "☃😀".into(),
797            interpolations: vec![]
798        })
799    );
800    test_error_kind!(
801        string_invalid_unicode_code_point,
802        r#""\u{110000}""#,
803        SyntaxErrorKind::InvalidUnicodeCodePoint(0x110000)
804    );
805    test_error_kind!(
806        string_missing_unicode_code_point_opening_brace,
807        r#""\u41}""#,
808        SyntaxErrorKind::NomError(nom::error::ErrorKind::Tag)
809    );
810    test_error_kind!(
811        string_missing_unicode_code_point_closing_brace,
812        r#""\u{41""#,
813        SyntaxErrorKind::NomError(nom::error::ErrorKind::Tag)
814    );
815    test_error_kind!(
816        string_invalid_hex_digit,
817        r#""\xGG""#,
818        SyntaxErrorKind::InvalidHexDigit
819    );
820    test_error_kind!(
821        string_invalid_unicode_code_point_hex,
822        r#""\u{GG}""#,
823        SyntaxErrorKind::InvalidHexDigit
824    );
825    test_expression!(
826        string_interpolation,
827        r#""{42}""#,
828        Expression::String(StringLiteral {
829            value: "".into(),
830            interpolations: vec![(0, Box::new(Expression::Number("42".into())))]
831        })
832    );
833    test_expression!(
834        string_interpolation_with_text,
835        r#""Hello, {42}!""#,
836        Expression::String(StringLiteral {
837            value: "Hello, !".into(),
838            interpolations: vec![(7, Box::new(Expression::Number("42".into())))]
839        })
840    );
841    test_expression!(
842        string_newline,
843        r#""
844""#,
845        Expression::String(StringLiteral {
846            value: "\n".into(),
847            interpolations: vec![]
848        })
849    );
850
851    macro_rules! test_binary_expression {
852        ($input:expr, $expected:expr) => {
853            let (input, result) = parse_expression($input).unwrap();
854            assert_eq!(input, "");
855            assert_eq!(dbg!(result).to_string(), $expected);
856        };
857    }
858
859    #[test]
860    fn test_binary_expression() {
861        test_binary_expression!("1 + 2 * 3", "(1 + (2 * 3))");
862        test_binary_expression!("2 * 3 + 4", "((2 * 3) + 4)");
863        test_binary_expression!("2 ** 3 * 4", "((2 ** 3) * 4)");
864        test_binary_expression!("2 * 3 ** 4", "(2 * (3 ** 4))");
865
866        // Associativity tests
867        test_binary_expression!("1 + 2 + 3", "((1 + 2) + 3)"); // Left-to-right
868        test_binary_expression!("2 ** 3 ** 2", "(2 ** (3 ** 2))"); // Right-to-left
869        test_binary_expression!("10 / 5 / 2", "((10 / 5) / 2)"); // Left-to-right
870
871        // Unary operators
872        test_binary_expression!("-2 * 3", "((-2) * 3)");
873        test_binary_expression!("!1 && 2", "((!1) && 2)");
874        test_binary_expression!("~1 & 2", "((~1) & 2)");
875
876        // Bitwise operations
877        test_binary_expression!("1 | 2 & 3", "(1 | (2 & 3))");
878        test_binary_expression!("1 & 2 ^ 3", "((1 & 2) ^ 3)");
879        test_binary_expression!("1 | 2 | 3", "((1 | 2) | 3)");
880
881        // Logical operations
882        test_binary_expression!("1 || 2 && 3", "(1 || (2 && 3))");
883        test_binary_expression!("1 && 2 || 3 && 4", "((1 && 2) || (3 && 4))");
884
885        // Comparison operations
886        test_binary_expression!("1 < 2 == 3 > 4", "((1 < 2) == (3 > 4))");
887        test_binary_expression!("1 == 2 != 3 == 4", "(((1 == 2) != 3) == 4)");
888
889        // Shift operations
890        test_binary_expression!("1 << 2 + 3", "(1 << (2 + 3))");
891        test_binary_expression!("1 + 2 >> 3", "((1 + 2) >> 3)");
892
893        // Pipe operator
894        test_binary_expression!("1 |> 2 |> 3", "((1 |> 2) |> 3)");
895
896        // Complex combinations
897        test_binary_expression!("1 + 2 * 3 ** 4", "(1 + (2 * (3 ** 4)))");
898        test_binary_expression!(
899            "1 || 2 && 3 | 4 ^ 5 & 6",
900            "(1 || (2 && (3 | (4 ^ (5 & 6)))))"
901        );
902        test_binary_expression!(
903            "1 + 2 * 3 < 4 && 5 == 6",
904            "(((1 + (2 * 3)) < 4) && (5 == 6))"
905        );
906    }
907}