expression/expression.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
//! This example demonstrates a very simple way to parse numbers— using [TokenizerContext::try_parse_float].
//! This is a nice, simple way of parsing single-type numbers. [TokenizerContext::try_parse_integer] is another way to do this.
//!
//! For more complex languages, [TokenizerContext::try_parse_number] parses both integers and floats, or, if more control
//! is needed, [TokenizerContext::consume_standard_number] will return a [String] of the next number-like section of code for manual
//! parsing.
//!
//! Numbers are by-far the most time-consuming part of creating a custom parser, so using the built-in systems is recommended, at least
//! for prototyping.
#![allow(dead_code)]
use alkale::{common::error::ErrorNotification, token::Token, TokenizerContext};
/// These are the tokens that will be present in the result. [MathSymbol::Number] holds data while the
/// rest are more symbolic.
#[derive(Debug, Clone)]
enum MathSymbol {
Plus,
Minus,
Times,
Divide,
Modulo,
OpenParen,
CloseParen,
Number(f64),
}
fn main() {
use MathSymbol::*;
// The test program we're going to tokenize.
let program = "23 * (012 - 3) / 1_2_3 + 5e-3";
// The TokenizerContext for our example program.
let mut context = TokenizerContext::new(program.chars());
// While there are more characters in the source code...
while context.has_next() {
// If the next character is one of these, push its respective token.
let single_char_pushed = context.map_single_char_token(|char| match char {
'+' => Some(Plus),
'-' => Some(Minus),
'*' => Some(Times),
'/' => Some(Divide),
'%' => Some(Modulo),
'(' => Some(OpenParen),
')' => Some(CloseParen),
_ => None,
});
// If the above properly pushed a token, skip this iteration.
if single_char_pushed {
continue;
}
// Try to parse a floating point number (f64), throw an error or push the token as necessary.
if let Some((result, span)) = context.try_parse_float() {
if let Ok(value) = result {
context.push_token(Token::new(Number(value), span));
} else {
context.report(ErrorNotification(String::from("Malformed number."), span));
}
continue;
}
// If the above didn't occur, skip the next character. Throw an error if it isn't whitespace.
if let Some((char, span)) = context.next_span() {
if !char.is_whitespace() {
context.report(ErrorNotification(
format!("Unexpected character `{}` in expression.", char),
span,
));
}
}
}
// Print the result— this example should have no error notifications.
println!("{:#?}", context.result());
}