brane_dsl/parser/
literal.rs

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//  LITERAL.rs
//    by Lut99
//
//  Created:
//    10 Aug 2022, 15:39:44
//  Last edited:
//    31 Oct 2023, 10:45:42
//  Auto updated?
//    Yes
//
//  Description:
//!   Contains nom function(s) that parse literal tokens.
//

use std::num::NonZeroUsize;

use log::trace;
use nom::error::{ContextError, ParseError};
use nom::{IResult, Parser, branch, combinator as comb};

use super::ast::Literal;
use crate::scanner::{Token, Tokens};
use crate::spec::TextRange;
use crate::tag_token;


/***** HELPER FUNCTIONS *****/
/// Resolves escape strings in a string by, well, resolving them.
///
/// # Arguments
/// - `raw`: The string to resolve.
///
/// # Returns
/// The to-be-resolved string.
fn resolve_escape(raw: String) -> String {
    // Loop to add
    let mut res: String = String::with_capacity(raw.len());
    let mut escaped: bool = false;
    for c in raw.chars() {
        // Check if escaped
        if escaped {
            // We are; match a specific set of characters
            if c == '\\' || c == '"' || c == '\'' {
                res.push(c);
            } else if c == 'n' {
                res.push('\n');
            } else if c == 'r' {
                res.push('\r');
            } else if c == 't' {
                res.push('\t');
            } else {
                panic!("Encountered unknown escape character '{}'", c);
            }
            escaped = false;
        } else if c == '\\' {
            // Going into escape mode
            escaped = true;
        } else {
            res.push(c);
        }
    }

    // Done
    res
}





/***** LIBRARY *****/
/// Parses a literal Token to a Literal node in the AST.
///
/// # Arguments
/// - `input`: The list of tokens to parse from.
///
/// # Returns
/// The remaining list of tokens and the parsed Literal if there was anything to parse. Otherwise, a `nom::Error` is returned (which may be a real error or simply 'could not parse').
pub fn parse<'a, E: ParseError<Tokens<'a>> + ContextError<Tokens<'a>>>(input: Tokens<'a>) -> IResult<Tokens<'a>, Literal, E> {
    trace!("Attempting to parse literal expression");
    branch::alt((
        comb::map(tag_token!(Token::Null), |t| Literal::Null { range: TextRange::from(t.tok[0].inner()) }),
        comb::map(tag_token!(Token::Boolean), |t| Literal::Boolean {
            value: t.tok[0].as_bool(),

            range: TextRange::from(t.tok[0].inner()),
        }),
        comb::map(tag_token!(Token::Integer), |t| Literal::Integer {
            value: t.tok[0].as_i64(),

            range: TextRange::from(t.tok[0].inner()),
        }),
        comb::map(tag_token!(Token::Real), |t| Literal::Real {
            value: t.tok[0].as_f64(),

            range: TextRange::from(t.tok[0].inner()),
        }),
        comb::map(tag_token!(Token::String), |t| Literal::String {
            value: resolve_escape(t.tok[0].as_string()),

            range: {
                // Wrap one back and forth for the quotes
                let mut r = TextRange::from(t.tok[0].inner());
                r.start.col -= 1;
                r.end.col += 1;
                r
            },
        }),
        comb::map(tag_token!(Token::Unit), |t| Literal::Void { range: TextRange::from(t.tok[0].inner()) }),
    ))
    .parse(input)
}