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)
}