mirror of https://github.com/rm-dr/daisy
Added tokenizer
parent
ceb7ea1a1a
commit
0c32a94f98
|
@ -0,0 +1,113 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Token {
|
||||||
|
Negative,
|
||||||
|
StartGroup,
|
||||||
|
EndGroup,
|
||||||
|
Number(String),
|
||||||
|
Operator(String),
|
||||||
|
Word(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turn a string into a set of tokens.
|
||||||
|
/// Does not check syntax. Fails if `input` contains an invalid character.
|
||||||
|
//
|
||||||
|
// # Arguments:
|
||||||
|
// `input`: A string like `(-3*2.2)/3`
|
||||||
|
//
|
||||||
|
// # Returns:
|
||||||
|
// * `Ok(Vec<token>)` if we were successful.
|
||||||
|
// * `Err(())` if we couldn't tokenize this string.
|
||||||
|
pub fn tokenize(input: &String) -> Result<Vec<Token>, ()> {
|
||||||
|
let mut v: Vec<Token> = Vec::new();
|
||||||
|
let mut t: Option<Token> = None;
|
||||||
|
for c in input.chars() {
|
||||||
|
match c {
|
||||||
|
// Minus sign can be both a Negative and an Operator.
|
||||||
|
// Needs special treatment.
|
||||||
|
'-' => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); t = None; }
|
||||||
|
match v.last() {
|
||||||
|
// If previous token was any of the following,
|
||||||
|
// this is the "minus" operator
|
||||||
|
Some(Token::Number(_)) |
|
||||||
|
Some(Token::EndGroup) |
|
||||||
|
Some(Token::Word(_)) => {
|
||||||
|
v.push(Token::Operator(String::from(c)));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Otherwise, this is a negative sign.
|
||||||
|
_ => { t = Some(Token::Negative); }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Number.
|
||||||
|
// Commas act just like dots.
|
||||||
|
',' | '.' | '0'..='9' => {
|
||||||
|
match &mut t {
|
||||||
|
// If we're already building a number,
|
||||||
|
// append.
|
||||||
|
Some(Token::Number(val)) => {
|
||||||
|
val.push(if c == ',' {'.'} else {c});
|
||||||
|
},
|
||||||
|
|
||||||
|
// If we're not building a number, finalize
|
||||||
|
// previous token and start one.
|
||||||
|
_ => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); }
|
||||||
|
t = Some(Token::Number(String::from(c)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Word
|
||||||
|
'A'..='Z' |
|
||||||
|
'a'..='z' => {
|
||||||
|
match &mut t {
|
||||||
|
// If we're already building a number,
|
||||||
|
// append.
|
||||||
|
Some(Token::Word(val)) => {
|
||||||
|
val.push(c);
|
||||||
|
},
|
||||||
|
|
||||||
|
// If we're not building a number, finalize
|
||||||
|
// previous token and start one.
|
||||||
|
_ => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); }
|
||||||
|
t = Some(Token::Word(String::from(c)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Operation
|
||||||
|
// Always one character
|
||||||
|
'+' | '*' | '/' | '^' => {
|
||||||
|
// Finalize previous token
|
||||||
|
if t.is_some() { v.push(t.unwrap()); t = None; }
|
||||||
|
v.push(Token::Operator(String::from(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
// Always one character
|
||||||
|
'(' => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); t = None; }
|
||||||
|
v.push(Token::StartGroup);
|
||||||
|
},
|
||||||
|
')' => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); t = None; }
|
||||||
|
v.push(Token::EndGroup);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Space. Basic seperator.
|
||||||
|
' ' => {
|
||||||
|
if t.is_some() { v.push(t.unwrap()); t = None; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid token
|
||||||
|
_ => { return Err(()); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.is_some() { v.push(t.unwrap()); }
|
||||||
|
return Ok(v);
|
||||||
|
}
|
Loading…
Reference in New Issue