Add exactness indicator
This commit is contained in:
parent
9ac596187d
commit
00d8380eac
2 changed files with 142 additions and 9 deletions
132
src/lexer/mod.rs
132
src/lexer/mod.rs
|
@ -9,6 +9,7 @@ mod number;
|
|||
mod str;
|
||||
|
||||
use self::char::Lexable;
|
||||
use self::number::Exactness;
|
||||
use self::number::NumberBuilder;
|
||||
use self::number::Radix;
|
||||
use self::number::Sign;
|
||||
|
@ -25,7 +26,11 @@ enum State {
|
|||
Dot,
|
||||
Hash,
|
||||
Number,
|
||||
NumberExactness,
|
||||
NumberDecimal,
|
||||
NumberRadix,
|
||||
NumberSign,
|
||||
Sign,
|
||||
String,
|
||||
}
|
||||
|
||||
|
@ -112,7 +117,10 @@ impl Lexer {
|
|||
}
|
||||
|
||||
else if let Some(sign) = Sign::from_char(c) {
|
||||
*token = Some(Token::Identifier(c.to_string()));
|
||||
self.number_builder = NumberBuilder::new();
|
||||
self.number_builder.sign(sign);
|
||||
self.state = State::Sign;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_identifier_initial() {
|
||||
self.state = State::Identifier;
|
||||
|
@ -146,7 +154,7 @@ impl Lexer {
|
|||
/// Handle self.state == State::Identifier
|
||||
fn state_identifier(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_identifier_subsequent() {
|
||||
// State in Identifier state.
|
||||
// Stay in Identifier state.
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_identifier_delimiter() {
|
||||
|
@ -176,12 +184,22 @@ impl Lexer {
|
|||
|
||||
fn state_hash(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_boolean_true() || c.is_boolean_false() {
|
||||
self.advance();
|
||||
*token = Some(Token::Boolean(c.is_boolean_true()));
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_left_paren() {
|
||||
self.advance();
|
||||
*token = Some(Token::LeftVectorParen);
|
||||
self.advance();
|
||||
}
|
||||
else if let Some(radix) = Radix::from_char(c) {
|
||||
self.number_builder.radix(radix);
|
||||
self.state = State::NumberRadix;
|
||||
self.advance();
|
||||
}
|
||||
else if let Some(exactness) = Exactness::from_char(c) {
|
||||
self.number_builder.exact(exactness);
|
||||
self.state = State::NumberExactness;
|
||||
self.advance();
|
||||
}
|
||||
else {
|
||||
assert!(false, "Invalid token character: '{}'", c);
|
||||
|
@ -206,6 +224,21 @@ impl Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
fn state_number_exactness(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_hash() {
|
||||
self.state = State::Hash;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_digit(self.number_builder.radix_value()) {
|
||||
self.number_builder.extend_value(c);
|
||||
self.state = State::Number;
|
||||
self.advance();
|
||||
}
|
||||
else {
|
||||
assert!(false, "Invalid token character: '{}'", c);
|
||||
}
|
||||
}
|
||||
|
||||
fn state_number_decimal(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_digit(Radix::Dec.value()) {
|
||||
self.number_builder.extend_decimal_value(c);
|
||||
|
@ -220,6 +253,60 @@ impl Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
fn state_number_radix(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_digit(self.number_builder.radix_value()) {
|
||||
self.number_builder.extend_value(c);
|
||||
self.state = State::Number;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_dot() {
|
||||
self.state = State::NumberDecimal;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_hash() {
|
||||
self.state = State::Hash;
|
||||
self.advance();
|
||||
}
|
||||
else if let Some(sign) = Sign::from_char(c) {
|
||||
self.number_builder.sign(sign);
|
||||
self.state = State::NumberSign;
|
||||
self.advance();
|
||||
}
|
||||
else {
|
||||
assert!(false, "Invalid token character: '{}'", c);
|
||||
}
|
||||
}
|
||||
|
||||
fn state_number_sign(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_digit(self.number_builder.radix_value()) {
|
||||
self.number_builder.extend_value(c);
|
||||
self.state = State::Number;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_dot() {
|
||||
self.state = State::NumberDecimal;
|
||||
self.advance();
|
||||
}
|
||||
else {
|
||||
assert!(false, "Invalid token character: '{}'", c);
|
||||
}
|
||||
}
|
||||
|
||||
fn state_sign(&mut self, c: char, token: &mut Option<Token>) {
|
||||
if c.is_digit(Radix::Dec.value()) {
|
||||
self.number_builder.extend_value(c);
|
||||
self.state = State::Number;
|
||||
self.advance();
|
||||
}
|
||||
else if c.is_identifier_delimiter() {
|
||||
*token = Some(Token::Identifier(self.value()));
|
||||
self.retract();
|
||||
}
|
||||
else {
|
||||
assert!(false, "Invalid token character: '{}'", c);
|
||||
}
|
||||
}
|
||||
|
||||
fn state_string(&mut self, c: char, token: &mut Option<Token>) {
|
||||
self.advance();
|
||||
if c.is_string_quote() {
|
||||
|
@ -263,7 +350,11 @@ impl Iterator for Lexer {
|
|||
State::Dot => self.state_dot(c, &mut token),
|
||||
State::Hash => self.state_hash(c, &mut token),
|
||||
State::Number => self.state_number(c, &mut token),
|
||||
State::NumberExactness => self.state_number_exactness(c, &mut token),
|
||||
State::NumberDecimal => self.state_number_decimal(c, &mut token),
|
||||
State::NumberRadix => self.state_number_radix(c, &mut token),
|
||||
State::NumberSign => self.state_number_sign(c, &mut token),
|
||||
State::Sign => self.state_sign(c, &mut token),
|
||||
State::String => self.state_string(c, &mut token),
|
||||
State::Comment => self.state_comment(c, &mut token),
|
||||
}
|
||||
|
@ -331,12 +422,41 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn lexer_finds_numbers() {
|
||||
check_single_token("34", Token::Number(Number::new(34.0)));
|
||||
fn finds_numbers() {
|
||||
check_single_token(".34", Token::Number(Number::new(0.34)));
|
||||
check_single_token("0.34", Token::Number(Number::new(0.34)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_negative_numbers() {
|
||||
check_single_token("-3", Token::Number(Number::from_int(-3)));
|
||||
check_single_token("-0", Token::Number(Number::from_int(-0)));
|
||||
check_single_token("-0.56", Token::Number(Number::new(-0.56)));
|
||||
check_single_token("-3.14159", Token::Number(Number::new(-3.14159)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_bin_numbers() {
|
||||
check_single_token("#b0", Token::Number(Number::from_int(0b0)));
|
||||
check_single_token("#b01011", Token::Number(Number::from_int(0b01011)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_dec_numbers() {
|
||||
check_single_token("34", Token::Number(Number::new(34.0)));
|
||||
check_single_token("#d89", Token::Number(Number::from_int(89)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_oct_numbers() {
|
||||
check_single_token("#o45", Token::Number(Number::from_int(0o45)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_hex_numbers() {
|
||||
check_single_token("#h4A65", Token::Number(Number::from_int(0x4A65)));
|
||||
}
|
||||
|
||||
fn check_single_token(input: &str, expected: Token) {
|
||||
let mut lexer = Lexer::new(input);
|
||||
assert_next_token(&mut lexer, &expected);
|
||||
|
|
|
@ -21,9 +21,12 @@ pub enum Radix { Bin, Oct, Dec, Hex }
|
|||
#[derive(PartialEq, Debug)]
|
||||
pub enum Sign { Pos, Neg }
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Exactness { Exact, Inexact }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NumberBuilder {
|
||||
exact: bool,
|
||||
exact: Exactness,
|
||||
radix: Radix,
|
||||
sign: Sign,
|
||||
value: f64,
|
||||
|
@ -33,7 +36,7 @@ pub struct NumberBuilder {
|
|||
impl NumberBuilder {
|
||||
pub fn new() -> NumberBuilder {
|
||||
NumberBuilder {
|
||||
exact: false,
|
||||
exact: Exactness::Inexact,
|
||||
radix: Radix::Dec,
|
||||
sign: Sign::Pos,
|
||||
value: 0.0,
|
||||
|
@ -41,7 +44,7 @@ impl NumberBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn exact<'a>(&'a mut self, ex: bool) -> &'a mut NumberBuilder {
|
||||
pub fn exact<'a>(&'a mut self, ex: Exactness) -> &'a mut NumberBuilder {
|
||||
self.exact = ex;
|
||||
self
|
||||
}
|
||||
|
@ -129,6 +132,16 @@ impl Sign {
|
|||
}
|
||||
}
|
||||
|
||||
impl Exactness {
|
||||
pub fn from_char(c: char) -> Option<Exactness> {
|
||||
match c {
|
||||
'i' => Some(Exactness::Inexact),
|
||||
'e' => Some(Exactness::Exact),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue