romulus/lex/
utils.rs

1///! Utilities for the lex function.
2///! Handles low level character gathering
3use std::iter::Peekable;
4use std::ops::RangeInclusive;
5
6pub trait Chomper {
7    fn accept(&self, ch: char) -> bool;
8}
9
10pub fn chomp<Iter>(chomper: &dyn Chomper, peekable: &mut Peekable<Iter>) -> usize
11where
12    Iter: Iterator<Item = (usize, char)>,
13{
14    let mut cur = 0;
15    while let Some((idx, ch)) = peekable.peek() {
16        cur = *idx;
17
18        if !chomper.accept(*ch) {
19            return cur;
20        }
21
22        peekable.next();
23    }
24
25    cur + 1
26}
27
28pub fn chomp_until<Iter>(chomper: &dyn Chomper, peekable: &mut Peekable<Iter>) -> usize
29where
30    Iter: Iterator<Item = (usize, char)>,
31{
32    let mut cur = 0;
33    while let Some((idx, ch)) = peekable.peek() {
34        cur = *idx;
35
36        if chomper.accept(*ch) {
37            return cur;
38        }
39
40        peekable.next();
41    }
42
43    cur + 1
44}
45
46pub fn chomp_str<Iter>(chomper: &dyn Chomper, peekable: &mut Peekable<Iter>) -> String
47where
48    Iter: Iterator<Item = (usize, char)>,
49{
50    let mut accepted = String::new();
51
52    while let Some((_, ch)) = peekable.peek() {
53        if chomper.accept(*ch) {
54            accepted.push(*ch);
55            peekable.next();
56        } else {
57            break;
58        }
59    }
60
61    accepted
62}
63
64pub struct Multi<'a, C: Chomper>(pub &'a [&'a C]);
65
66impl Chomper for RangeInclusive<char> {
67    fn accept(&self, ch: char) -> bool {
68        self.contains(&ch)
69    }
70}
71
72impl Chomper for [char] {
73    fn accept(&self, ch: char) -> bool {
74        self.contains(&ch)
75    }
76}
77
78impl Chomper for &[char; 1] {
79    fn accept(&self, ch: char) -> bool {
80        self.contains(&ch)
81    }
82}
83
84impl Chomper for &[char; 2] {
85    fn accept(&self, ch: char) -> bool {
86        self.contains(&ch)
87    }
88}
89
90impl Chomper for &[char; 3] {
91    fn accept(&self, ch: char) -> bool {
92        self.contains(&ch)
93    }
94}
95
96impl<C: Chomper> Chomper for Multi<'_, C> {
97    fn accept(&self, ch: char) -> bool {
98        for sub in self.0 {
99            if sub.accept(ch) {
100                return true;
101            }
102        }
103
104        false
105    }
106}
107
108impl<A: Chomper, B: Chomper> Chomper for (A, B) {
109    fn accept(&self, ch: char) -> bool {
110        self.0.accept(ch) || self.1.accept(ch)
111    }
112}
113
114/// gather characters until the terminating character
115/// if this range of characters is meant to be evaluated with $
116/// then \$ is kept together to distignuish it from actual evaultion
117/// at runtime.
118#[inline]
119pub fn chomp_until_escaped<T: Iterator<Item = (usize, char)>>(
120    iter: &mut Peekable<T>,
121    terminator: char,
122    escapable: &[char], //evaluates: bool,
123) -> Result<String, String> {
124    let mut accepted = String::new();
125
126    while let Some((_, ch)) = &mut iter.peek() {
127        let owned: char = *ch;
128
129        if owned == '\\' {
130            iter.next();
131            match iter.next() {
132                Some((_, escaped)) if escaped == terminator => accepted.push(escaped),
133                Some((_, escaped)) if escapable.contains(&escaped) => {
134                    accepted.push('\\');
135                    accepted.push(escaped);
136                }
137
138                Some((_, 'n')) => accepted.push('\n'),
139                Some((_, 't')) => accepted.push('\t'),
140                Some((_, 'r')) => accepted.push('\r'),
141                Some((_, '\\')) => accepted.push('\\'),
142                Some((_, escaped)) => return Err(format!("cannot escape {}", escaped)),
143                None => return Err(format!("found EOF when searching for {}", &terminator)),
144            }
145        } else if owned != terminator {
146            accepted.push(owned);
147            iter.next();
148        } else {
149            break;
150        }
151    }
152
153    Ok(accepted)
154}
155
156/// Evaulate the character buffer as a number
157#[inline]
158pub fn get_number(vec: &str) -> i64 {
159    let mut buffer = 0;
160    for ch in vec.chars() {
161        let digit = ch.to_string().parse::<i64>().unwrap();
162
163        buffer = buffer * 10 + digit;
164    }
165
166    buffer
167}