[lexer] Make a states::number module
There will be several number-specific states.
This commit is contained in:
parent
14ea07e441
commit
469929fb8f
2 changed files with 238 additions and 45 deletions
|
@ -1,45 +0,0 @@
|
|||
/* lexer/src/states/number.rs
|
||||
* Eryn Wells <eryn@erynwells.me>
|
||||
*/
|
||||
|
||||
use super::{State, StateResult, Token};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum Base {
|
||||
Bin = 2,
|
||||
Oct = 8,
|
||||
Dec = 10,
|
||||
Hex = 16
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
base: Base
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BeginNumber(Builder);
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Builder {
|
||||
Builder { base: Base::Dec }
|
||||
}
|
||||
}
|
||||
|
||||
impl BeginNumber {
|
||||
pub fn new() -> BeginNumber {
|
||||
BeginNumber(Builder::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl State for BeginNumber {
|
||||
fn lex(&mut self, c: char) -> StateResult {
|
||||
// TODO: Implement.
|
||||
StateResult::fail("blah")
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<Option<Token>, String> {
|
||||
// TODO: Implement.
|
||||
Err("blah".to_string())
|
||||
}
|
||||
}
|
238
lexer/src/states/number/mod.rs
Normal file
238
lexer/src/states/number/mod.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
/* lexer/src/states/number.rs
|
||||
* Eryn Wells <eryn@erynwells.me>
|
||||
*/
|
||||
|
||||
use std::collections::HashSet;
|
||||
use chars::Lexable;
|
||||
use super::{Resume, State, StateResult, Token};
|
||||
|
||||
trait NumberLexable {
|
||||
/// Returns the value of this character interpreted as the indicator for a
|
||||
/// base. In Scheme, you indicate the base of a number by prefixing it with
|
||||
/// #[bodx].
|
||||
fn base_value(&self) -> Option<Base>;
|
||||
/// Returns the value of the character interpreted as a numerical digit.
|
||||
fn digit_value(&self) -> Option<u8>;
|
||||
fn sign_value(&self) -> Option<Sign>;
|
||||
fn is_dot(&self) -> bool;
|
||||
fn is_hash(&self) -> bool;
|
||||
fn is_sign(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Base { Bin = 2, Oct = 8, Dec = 10, Hex = 16 }
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Sign { Neg = -1, Pos = 1 }
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Builder {
|
||||
base: Option<Base>,
|
||||
sign: Option<Sign>,
|
||||
value: i64
|
||||
}
|
||||
|
||||
#[derive(Debug)] pub struct BeginState(Builder);
|
||||
#[derive(Debug)] pub struct DigitState(Builder);
|
||||
#[derive(Debug)] pub struct HashState(Builder);
|
||||
#[derive(Debug)] pub struct SignState(Builder);
|
||||
|
||||
impl Base {
|
||||
pub fn contains(&self, digit: u8) -> bool {
|
||||
digit < (*self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
base: None,
|
||||
sign: None,
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn base(&self) -> Base {
|
||||
match self.base {
|
||||
Some(b) => b,
|
||||
None => Base::Dec
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(&self) -> Sign {
|
||||
match self.sign {
|
||||
Some(s) => s,
|
||||
None => Sign::Pos
|
||||
}
|
||||
}
|
||||
|
||||
fn push_base(&mut self, base: Base) {
|
||||
self.base = Some(base);
|
||||
}
|
||||
|
||||
fn push_sign(&mut self, sign: Sign) {
|
||||
self.sign = Some(sign);
|
||||
}
|
||||
|
||||
fn push_digit(&mut self, digit: u8) {
|
||||
self.value = self.value * self.base_value() as i64 + digit as i64;
|
||||
}
|
||||
|
||||
fn resolve(&self) -> i64 {
|
||||
let sign_factor: i64 = if let Some(sign) = self.sign { sign as i64 } else { 1 };
|
||||
self.value * sign_factor
|
||||
}
|
||||
|
||||
fn seen_base(&self) -> bool { self.base.is_some() }
|
||||
|
||||
fn base_value(&self) -> u8 { self.base() as u8 }
|
||||
}
|
||||
|
||||
impl BeginState {
|
||||
pub fn new() -> BeginState {
|
||||
BeginState (Builder::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl State for BeginState {
|
||||
fn lex(&mut self, c: char) -> StateResult {
|
||||
match c {
|
||||
c if c.is_hash() => StateResult::advance(Box::new(HashState(self.0))),
|
||||
c if c.is_sign() => {
|
||||
self.0.push_sign(c.sign_value().unwrap());
|
||||
StateResult::advance(Box::new(SignState(self.0)))
|
||||
},
|
||||
c if c.is_digit(self.0.base_value() as u32) => {
|
||||
let value = c.digit_value().unwrap();
|
||||
if self.0.base().contains(value) {
|
||||
self.0.push_digit(value);
|
||||
StateResult::advance(Box::new(DigitState(self.0)))
|
||||
} else {
|
||||
StateResult::fail(format!("invalid digit for current base: {}", c).as_str())
|
||||
}
|
||||
},
|
||||
_ => StateResult::fail(format!("invalid char: {}", c).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<Option<Token>, String> {
|
||||
// TODO: Implement.
|
||||
Err("blah".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl State for HashState {
|
||||
fn lex(&mut self, c: char) -> StateResult {
|
||||
if let Some(base) = c.base_value() {
|
||||
if !self.0.seen_base() {
|
||||
self.0.push_base(base);
|
||||
StateResult::advance(Box::new(BeginState (self.0)))
|
||||
} else {
|
||||
StateResult::fail("got base again, despite already having one")
|
||||
}
|
||||
} else {
|
||||
StateResult::fail(format!("invalid char: {}", c).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<Option<Token>, String> {
|
||||
// TODO: Implement.
|
||||
Err("blah".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl SignState {
|
||||
pub fn initials() -> HashSet<char> {
|
||||
let mut inits = HashSet::new();
|
||||
inits.insert('+');
|
||||
inits.insert('-');
|
||||
inits
|
||||
}
|
||||
}
|
||||
|
||||
impl State for SignState {
|
||||
fn lex(&mut self, c: char) -> StateResult {
|
||||
if let Some(digit) = c.digit_value() {
|
||||
if self.0.base().contains(digit) {
|
||||
self.0.push_digit(digit);
|
||||
StateResult::advance(Box::new(DigitState(self.0)))
|
||||
} else {
|
||||
StateResult::fail(format!("invalid digit for current base: {}", c).as_str())
|
||||
}
|
||||
} else {
|
||||
StateResult::fail(format!("invalid char: {}", c).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<Option<Token>, String> {
|
||||
// TODO: Implement.
|
||||
Err("blah".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl DigitState {
|
||||
pub fn initials() -> HashSet<char> {
|
||||
let foldp = |acc: HashSet<char>, x: u8| {
|
||||
let c = char::from(x);
|
||||
acc.insert(c);
|
||||
acc
|
||||
};
|
||||
'0'..='9'.chain(('a' as u8)..=('f' as u8)).fold(HashSet::new(), foldp)
|
||||
}
|
||||
}
|
||||
|
||||
impl State for DigitState {
|
||||
fn lex(&mut self, c: char) -> StateResult {
|
||||
if let Some(digit) = c.digit_value() {
|
||||
if self.0.base().contains(digit) {
|
||||
self.0.push_digit(digit);
|
||||
StateResult::Continue
|
||||
} else {
|
||||
StateResult::fail(format!("invalid digit for current base: {}", c).as_str())
|
||||
}
|
||||
} else if c.is_identifier_delimiter() {
|
||||
StateResult::emit(Token::Num(self.0.resolve()), Resume::Here)
|
||||
} else {
|
||||
StateResult::fail(format!("invalid char: {}", c).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<Option<Token>, String> {
|
||||
// TODO: Implement.
|
||||
Err("blah".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl NumberLexable for char {
|
||||
fn base_value(&self) -> Option<Base> {
|
||||
match *self {
|
||||
'b' => Some(Base::Bin),
|
||||
'o' => Some(Base::Oct),
|
||||
'd' => Some(Base::Dec),
|
||||
'x' => Some(Base::Hex),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn digit_value(&self) -> Option<u8> {
|
||||
let ascii_value = *self as u32;
|
||||
match *self {
|
||||
'0'...'9' => Some((ascii_value - '0' as u32) as u8),
|
||||
'a'...'f' => Some((ascii_value - 'a' as u32 + 10) as u8),
|
||||
'A'...'F' => Some((ascii_value - 'A' as u32 + 10) as u8),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_value(&self) -> Option<Sign> {
|
||||
match *self {
|
||||
'+' => Some(Sign::Pos),
|
||||
'-' => Some(Sign::Neg),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dot(&self) -> bool { *self == '.' }
|
||||
fn is_hash(&self) -> bool { *self == '#' }
|
||||
fn is_sign(&self) -> bool { self.sign_value().is_some() }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue