Move the lexer to its own sibillexer module
Lots of failing tests right now, unfortunately. :(
This commit is contained in:
parent
cea63e8e8e
commit
fc947280ae
9 changed files with 264 additions and 244 deletions
7
lexer/Cargo.toml
Normal file
7
lexer/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "sibillexer"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Eryn Wells <eryn@erynwells.me>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sibiltypes = { path = "../types" }
|
|
@ -2,7 +2,7 @@
|
||||||
* Eryn Wells <eryn@erynwells.me>
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use lexer::charset;
|
use charset;
|
||||||
|
|
||||||
pub trait Lexable {
|
pub trait Lexable {
|
||||||
fn is_character_leader(&self) -> bool;
|
fn is_character_leader(&self) -> bool;
|
|
@ -2,69 +2,19 @@
|
||||||
* Eryn Wells <eryn@erynwells.me>
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod token;
|
|
||||||
pub use self::token::Lex;
|
|
||||||
pub use self::token::Token;
|
|
||||||
|
|
||||||
mod char;
|
|
||||||
mod charset;
|
|
||||||
mod number;
|
|
||||||
mod str;
|
|
||||||
|
|
||||||
mod named_char {
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use types::Char;
|
|
||||||
|
|
||||||
const ALARM: &'static str = "alarm";
|
|
||||||
const BACKSPACE: &'static str = "backspace";
|
|
||||||
const DELETE: &'static str = "delete";
|
|
||||||
const ESCAPE: &'static str = "escape";
|
|
||||||
const NEWLINE: &'static str = "newline";
|
|
||||||
const NULL: &'static str = "null";
|
|
||||||
const RETURN: &'static str = "return";
|
|
||||||
const SPACE: &'static str = "space";
|
|
||||||
const TAB: &'static str = "tab";
|
|
||||||
|
|
||||||
pub fn set() -> HashSet<&'static str> {
|
|
||||||
let mut set: HashSet<&'static str> = HashSet::new();
|
|
||||||
set.insert(ALARM);
|
|
||||||
set.insert(BACKSPACE);
|
|
||||||
set.insert(DELETE);
|
|
||||||
set.insert(ESCAPE);
|
|
||||||
set.insert(NEWLINE);
|
|
||||||
set.insert(NULL);
|
|
||||||
set.insert(RETURN);
|
|
||||||
set.insert(SPACE);
|
|
||||||
set.insert(TAB);
|
|
||||||
set
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn char_named_by(named: &str) -> Char {
|
|
||||||
Char::new(match named {
|
|
||||||
ALARM => '\x07',
|
|
||||||
BACKSPACE => '\x08',
|
|
||||||
DELETE => '\x7F',
|
|
||||||
ESCAPE => '\x1B',
|
|
||||||
NEWLINE => '\n',
|
|
||||||
NULL => '\0',
|
|
||||||
RETURN => '\r',
|
|
||||||
SPACE => ' ',
|
|
||||||
TAB => '\t',
|
|
||||||
_ => panic!("char_named_by called with invalid named char string")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use sibiltypes::{Bool, Char};
|
||||||
|
|
||||||
use types::{Bool, Char};
|
use char::Lexable;
|
||||||
use self::char::Lexable;
|
use named_char;
|
||||||
use self::number::Exactness;
|
use number::Exactness;
|
||||||
use self::number::NumberBuilder;
|
use number::NumberBuilder;
|
||||||
use self::number::Radix;
|
use number::Radix;
|
||||||
use self::number::Sign;
|
use number::Sign;
|
||||||
use self::str::CharAt;
|
use str::CharAt;
|
||||||
use self::str::RelativeIndexable;
|
use str::RelativeIndexable;
|
||||||
|
use token::Lex;
|
||||||
|
use token::Token;
|
||||||
|
|
||||||
type StateResult = Result<Option<Token>, String>;
|
type StateResult = Result<Option<Token>, String>;
|
||||||
|
|
||||||
|
@ -265,7 +215,7 @@ impl Lexer {
|
||||||
if candidates.len() > 0 {
|
if candidates.len() > 0 {
|
||||||
self.state = State::NamedChar(candidates, lower_c);
|
self.state = State::NamedChar(candidates, lower_c);
|
||||||
} else {
|
} else {
|
||||||
return self.token_result(Token::Character(Char::new(c)));
|
return self.token_result(Token::Character(Char(c)));
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -280,7 +230,7 @@ impl Lexer {
|
||||||
if c.is_identifier_delimiter() || c.is_eof() {
|
if c.is_identifier_delimiter() || c.is_eof() {
|
||||||
if progress.len() == 1 {
|
if progress.len() == 1 {
|
||||||
self.retract();
|
self.retract();
|
||||||
return self.token_result(Token::Character(Char::new(progress.chars().next().unwrap())));
|
return self.token_result(Token::Character(Char(progress.chars().next().unwrap())));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return self.generic_error(c);
|
return self.generic_error(c);
|
||||||
|
@ -337,7 +287,7 @@ impl Lexer {
|
||||||
fn state_hash(&mut self, c: char) -> StateResult {
|
fn state_hash(&mut self, c: char) -> StateResult {
|
||||||
if c.is_boolean_true() || c.is_boolean_false() {
|
if c.is_boolean_true() || c.is_boolean_false() {
|
||||||
self.advance();
|
self.advance();
|
||||||
return self.token_result(Token::Boolean(Bool::new(c.is_boolean_true())));
|
return self.token_result(Token::Boolean(Bool(c.is_boolean_true())));
|
||||||
}
|
}
|
||||||
else if c.is_left_paren() {
|
else if c.is_left_paren() {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
@ -580,166 +530,3 @@ impl HasResult for StateResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// UNIT TESTING
|
|
||||||
//
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use types::{Bool, Char, Number};
|
|
||||||
use std::iter::Iterator;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_parens() {
|
|
||||||
check_single_token("(", Token::LeftParen);
|
|
||||||
check_single_token(")", Token::RightParen);
|
|
||||||
check_single_token("#(", Token::LeftVectorParen);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_characters() {
|
|
||||||
check_single_token("#\\a", Token::Character(Char::new('a')));
|
|
||||||
check_single_token("#\\n", Token::Character(Char::new('n')));
|
|
||||||
check_single_token("#\\s", Token::Character(Char::new('s')));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_named_characters() {
|
|
||||||
check_single_token("#\\newline", Token::Character(Char::new('\n')));
|
|
||||||
check_single_token("#\\null", Token::Character(Char::new('\0')));
|
|
||||||
check_single_token("#\\space", Token::Character(Char::new(' ')));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_dots() {
|
|
||||||
check_single_token(".", Token::Dot);
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new("abc . abc");
|
|
||||||
assert_next_token(&mut lexer, &Token::Id(String::from("abc")));
|
|
||||||
assert_next_token(&mut lexer, &Token::Dot);
|
|
||||||
assert_next_token(&mut lexer, &Token::Id(String::from("abc")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_identifiers() {
|
|
||||||
let tok = |s: &str| { check_single_token(s, Token::Id(String::from(s))); };
|
|
||||||
tok("abc");
|
|
||||||
tok("number?");
|
|
||||||
tok("+");
|
|
||||||
tok("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_booleans() {
|
|
||||||
check_single_token("#t", Token::Boolean(Bool::new(true)));
|
|
||||||
check_single_token("#f", Token::Boolean(Bool::new(false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_comments() {
|
|
||||||
let s = "; a comment";
|
|
||||||
check_single_token(s, Token::Comment(String::from(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_escaped_characters_in_strings() {
|
|
||||||
check_single_token("\"\\\\\"", Token::String(String::from("\\")));
|
|
||||||
check_single_token("\"\\\"\"", Token::String(String::from("\"")));
|
|
||||||
check_single_token("\"\\n\"", Token::String(String::from("\n")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_numbers() {
|
|
||||||
check_single_token("34", Token::Number(Number::from_float(34.0)));
|
|
||||||
check_single_token(".34", Token::Number(Number::from_float(0.34)));
|
|
||||||
check_single_token("0.34", Token::Number(Number::from_float(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::from_float(-0.56)));
|
|
||||||
check_single_token("-3.14159", Token::Number(Number::from_float(-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::from_float(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_exact_numbers() {
|
|
||||||
check_single_token("#e45", Token::Number(Number::from_int(45)));
|
|
||||||
check_single_token("#e-45", Token::Number(Number::from_int(-45)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_hex_numbers() {
|
|
||||||
check_single_token("#h4A65", Token::Number(Number::from_int(0x4A65)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_quote() {
|
|
||||||
check_single_token("'", Token::Quote);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn finds_strings() {
|
|
||||||
check_single_token("\"\"", Token::String(String::from("")));
|
|
||||||
check_single_token("\"abc\"", Token::String(String::from("abc")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lexes_simple_expression() {
|
|
||||||
check_tokens("(+ 3.4 6.8)", vec![
|
|
||||||
Token::LeftParen,
|
|
||||||
Token::Id(String::from("+")),
|
|
||||||
Token::Number(Number::from_float(3.4)),
|
|
||||||
Token::Number(Number::from_float(6.8)),
|
|
||||||
Token::RightParen]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lexes_quoted_identifier() {
|
|
||||||
check_tokens("'abc", vec![Token::Quote, Token::Id(String::from("abc"))]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_single_token(input: &str, expected: Token) {
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
assert_next_token(&mut lexer, &expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_tokens(input: &str, expected: Vec<Token>) {
|
|
||||||
let lexer = Lexer::new(input);
|
|
||||||
let mut expected_iter = expected.iter();
|
|
||||||
for lex in lexer {
|
|
||||||
if let Some(expected_token) = expected_iter.next() {
|
|
||||||
assert_eq!(lex.token, *expected_token);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert!(false, "Found a token we didn't expect: {:?}", lex.token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: Check that all expected tokens are consumed.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_next_token(lexer: &mut Lexer, expected: &Token) {
|
|
||||||
let lex = lexer.next().unwrap();
|
|
||||||
assert_eq!(lex.token, *expected);
|
|
||||||
}
|
|
||||||
}
|
|
169
lexer/src/lib.rs
Normal file
169
lexer/src/lib.rs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
extern crate sibiltypes;
|
||||||
|
|
||||||
|
mod char;
|
||||||
|
mod charset;
|
||||||
|
mod lexer;
|
||||||
|
mod named_char;
|
||||||
|
mod number;
|
||||||
|
mod str;
|
||||||
|
mod token;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use sibiltypes::{Bool, Char, Number};
|
||||||
|
use std::iter::Iterator;
|
||||||
|
use lexer::Lexer;
|
||||||
|
use token::Token;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_parens() {
|
||||||
|
check_single_token("(", Token::LeftParen);
|
||||||
|
check_single_token(")", Token::RightParen);
|
||||||
|
check_single_token("#(", Token::LeftVectorParen);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_characters() {
|
||||||
|
check_single_token("#\\a", Token::Character(Char('a')));
|
||||||
|
check_single_token("#\\n", Token::Character(Char('n')));
|
||||||
|
check_single_token("#\\s", Token::Character(Char('s')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_named_characters() {
|
||||||
|
check_single_token("#\\newline", Token::Character(Char('\n')));
|
||||||
|
check_single_token("#\\null", Token::Character(Char('\0')));
|
||||||
|
check_single_token("#\\space", Token::Character(Char(' ')));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_dots() {
|
||||||
|
check_single_token(".", Token::Dot);
|
||||||
|
|
||||||
|
let mut lexer = Lexer::new("abc . abc");
|
||||||
|
assert_next_token(&mut lexer, &Token::Id(String::from("abc")));
|
||||||
|
assert_next_token(&mut lexer, &Token::Dot);
|
||||||
|
assert_next_token(&mut lexer, &Token::Id(String::from("abc")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_identifiers() {
|
||||||
|
let tok = |s: &str| { check_single_token(s, Token::Id(String::from(s))); };
|
||||||
|
tok("abc");
|
||||||
|
tok("number?");
|
||||||
|
tok("+");
|
||||||
|
tok("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_booleans() {
|
||||||
|
check_single_token("#t", Token::Boolean(Bool(true)));
|
||||||
|
check_single_token("#f", Token::Boolean(Bool(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_comments() {
|
||||||
|
let s = "; a comment";
|
||||||
|
check_single_token(s, Token::Comment(String::from(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_escaped_characters_in_strings() {
|
||||||
|
check_single_token("\"\\\\\"", Token::String(String::from("\\")));
|
||||||
|
check_single_token("\"\\\"\"", Token::String(String::from("\"")));
|
||||||
|
check_single_token("\"\\n\"", Token::String(String::from("\n")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_numbers() {
|
||||||
|
check_single_token("34", Token::Number(Number::from_int(34, true)));
|
||||||
|
check_single_token(".34", Token::Number(Number::from_float(0.34, false)));
|
||||||
|
check_single_token("0.34", Token::Number(Number::from_float(0.34, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_negative_numbers() {
|
||||||
|
check_single_token("-3", Token::Number(Number::from_int(-3, true)));
|
||||||
|
check_single_token("-0", Token::Number(Number::from_int(-0, true)));
|
||||||
|
check_single_token("-0.56", Token::Number(Number::from_float(-0.56, false)));
|
||||||
|
check_single_token("-3.14159", Token::Number(Number::from_float(-3.14159, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_bin_numbers() {
|
||||||
|
check_single_token("#b0", Token::Number(Number::from_int(0b0, true)));
|
||||||
|
check_single_token("#b01011", Token::Number(Number::from_int(0b01011, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_dec_numbers() {
|
||||||
|
check_single_token("34", Token::Number(Number::from_int(34, true)));
|
||||||
|
check_single_token("#d89", Token::Number(Number::from_int(89, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_oct_numbers() {
|
||||||
|
check_single_token("#o45", Token::Number(Number::from_int(0o45, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_exact_numbers() {
|
||||||
|
check_single_token("#e45", Token::Number(Number::from_int(45, true)));
|
||||||
|
check_single_token("#e-45", Token::Number(Number::from_int(-45, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_hex_numbers() {
|
||||||
|
check_single_token("#h4A65", Token::Number(Number::from_int(0x4A65, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_quote() {
|
||||||
|
check_single_token("'", Token::Quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_strings() {
|
||||||
|
check_single_token("\"\"", Token::String(String::from("")));
|
||||||
|
check_single_token("\"abc\"", Token::String(String::from("abc")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lexes_simple_expression() {
|
||||||
|
check_tokens("(+ 3.4 6.8)", vec![
|
||||||
|
Token::LeftParen,
|
||||||
|
Token::Id(String::from("+")),
|
||||||
|
Token::Number(Number::from_float(3.4, false)),
|
||||||
|
Token::Number(Number::from_float(6.8, false)),
|
||||||
|
Token::RightParen]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lexes_quoted_identifier() {
|
||||||
|
check_tokens("'abc", vec![Token::Quote, Token::Id(String::from("abc"))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_single_token(input: &str, expected: Token) {
|
||||||
|
let mut lexer = Lexer::new(input);
|
||||||
|
assert_next_token(&mut lexer, &expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_tokens(input: &str, expected: Vec<Token>) {
|
||||||
|
let lexer = Lexer::new(input);
|
||||||
|
let mut expected_iter = expected.iter();
|
||||||
|
for lex in lexer {
|
||||||
|
if let Some(expected_token) = expected_iter.next() {
|
||||||
|
assert_eq!(lex.token, *expected_token);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert!(false, "Found a token we didn't expect: {:?}", lex.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Check that all expected tokens are consumed.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_next_token(lexer: &mut Lexer, expected: &Token) {
|
||||||
|
let lex = lexer.next().unwrap();
|
||||||
|
assert_eq!(lex.token, *expected);
|
||||||
|
}
|
||||||
|
}
|
45
lexer/src/named_char.rs
Normal file
45
lexer/src/named_char.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/* lexer/src/named_char.rs
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use sibiltypes::Char;
|
||||||
|
|
||||||
|
const ALARM: &'static str = "alarm";
|
||||||
|
const BACKSPACE: &'static str = "backspace";
|
||||||
|
const DELETE: &'static str = "delete";
|
||||||
|
const ESCAPE: &'static str = "escape";
|
||||||
|
const NEWLINE: &'static str = "newline";
|
||||||
|
const NULL: &'static str = "null";
|
||||||
|
const RETURN: &'static str = "return";
|
||||||
|
const SPACE: &'static str = "space";
|
||||||
|
const TAB: &'static str = "tab";
|
||||||
|
|
||||||
|
pub fn set() -> HashSet<&'static str> {
|
||||||
|
let mut set: HashSet<&'static str> = HashSet::new();
|
||||||
|
set.insert(ALARM);
|
||||||
|
set.insert(BACKSPACE);
|
||||||
|
set.insert(DELETE);
|
||||||
|
set.insert(ESCAPE);
|
||||||
|
set.insert(NEWLINE);
|
||||||
|
set.insert(NULL);
|
||||||
|
set.insert(RETURN);
|
||||||
|
set.insert(SPACE);
|
||||||
|
set.insert(TAB);
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn char_named_by(named: &str) -> Char {
|
||||||
|
Char(match named {
|
||||||
|
ALARM => '\x07',
|
||||||
|
BACKSPACE => '\x08',
|
||||||
|
DELETE => '\x7F',
|
||||||
|
ESCAPE => '\x1B',
|
||||||
|
NEWLINE => '\n',
|
||||||
|
NULL => '\0',
|
||||||
|
RETURN => '\r',
|
||||||
|
SPACE => ' ',
|
||||||
|
TAB => '\t',
|
||||||
|
_ => panic!("char_named_by called with invalid named char string")
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
* Eryn Wells <eryn@erynwells.me>
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use types::Number;
|
use sibiltypes::Number;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Radix { Bin, Oct, Dec, Hex }
|
pub enum Radix { Bin, Oct, Dec, Hex }
|
||||||
|
@ -67,9 +67,20 @@ impl NumberBuilder {
|
||||||
|
|
||||||
pub fn resolve(&self) -> Number {
|
pub fn resolve(&self) -> Number {
|
||||||
// TODO: Convert fields to Number type.
|
// TODO: Convert fields to Number type.
|
||||||
let value = if self.point > 0 { self.value / 10u32.pow(self.point) as f64 } else { self.value };
|
let value = if self.point > 0 {
|
||||||
let value = if self.sign == Sign::Neg { value * -1.0 } else { value };
|
self.value / 10u32.pow(self.point) as f64
|
||||||
Number::from_float(value)
|
}
|
||||||
|
else {
|
||||||
|
self.value
|
||||||
|
};
|
||||||
|
let value = if self.sign == Sign::Neg {
|
||||||
|
value * -1.0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value
|
||||||
|
};
|
||||||
|
// TODO: Use an integer if we can.
|
||||||
|
Number::from_float(value, self.exact == Exactness::Exact)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn radix_value(&self) -> u32 {
|
pub fn radix_value(&self) -> u32 {
|
||||||
|
@ -133,42 +144,43 @@ impl Exactness {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use sibiltypes::Number;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builds_integers() {
|
fn builds_integers() {
|
||||||
let mut b = NumberBuilder::new();
|
let mut b = NumberBuilder::new();
|
||||||
b.extend_value('3');
|
b.extend_value('3');
|
||||||
assert_eq!(b.resolve().value, 3.0);
|
assert_eq!(b.resolve(), Number::from_int(3, true));
|
||||||
b.extend_value('4');
|
b.extend_value('4');
|
||||||
assert_eq!(b.resolve().value, 34.0);
|
assert_eq!(b.resolve(), Number::from_int(34, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builds_negative_integers() {
|
fn builds_negative_integers() {
|
||||||
let num = NumberBuilder::new().sign(Sign::Neg).extend_value('3').resolve();
|
let num = NumberBuilder::new().sign(Sign::Neg).extend_value('3').resolve();
|
||||||
assert_eq!(num.value, -3.0);
|
assert_eq!(num, Number::from_int(-3, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builds_pointy_numbers() {
|
fn builds_pointy_numbers() {
|
||||||
let mut b = NumberBuilder::new();
|
let mut b = NumberBuilder::new();
|
||||||
b.extend_value('5');
|
b.extend_value('5');
|
||||||
assert_eq!(b.resolve().value, 5.0);
|
assert_eq!(b.resolve(), Number::from_int(5, true));
|
||||||
b.extend_decimal_value('3');
|
b.extend_decimal_value('3');
|
||||||
assert_eq!(b.resolve().value, 5.3);
|
assert_eq!(b.resolve(), Number::from_float(5.3, false));
|
||||||
b.extend_decimal_value('4');
|
b.extend_decimal_value('4');
|
||||||
assert_eq!(b.resolve().value, 5.34);
|
assert_eq!(b.resolve(), Number::from_float(5.34, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builds_hex() {
|
fn builds_hex() {
|
||||||
let mut b = NumberBuilder::new();
|
let mut b = NumberBuilder::new();
|
||||||
b.radix(Radix::Hex).extend_value('4');
|
b.radix(Radix::Hex).extend_value('4');
|
||||||
assert_eq!(b.resolve().value, 0x4 as f64);
|
assert_eq!(b.resolve(), Number::from_int(0x4, true));
|
||||||
b.extend_value('A');
|
b.extend_value('A');
|
||||||
assert_eq!(b.resolve().value, 0x4A as f64);
|
assert_eq!(b.resolve(), Number::from_int(0x4A, true));
|
||||||
b.extend_value('6');
|
b.extend_value('6');
|
||||||
assert_eq!(b.resolve().value, 0x4A6 as f64);
|
assert_eq!(b.resolve(), Number::from_int(0x4A6, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
* Eryn Wells <eryn@erynwells.me>
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use types::{Bool, Char, Number};
|
use sibiltypes::{Bool, Char, Number};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
|
@ -19,8 +19,8 @@ pub enum Token {
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Lex is a Token extracted from a specific position in an input. It contains useful information about the token's
|
/// A Lex is a Token extracted from a specific position in an input stream. It
|
||||||
/// place.
|
/// contains useful information about the token's place.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Lex {
|
pub struct Lex {
|
||||||
pub token: Token,
|
pub token: Token,
|
Loading…
Add table
Add a link
Reference in a new issue