✨ float and int values.
parent
6e94192fa9
commit
efdbafb645
|
@ -1,4 +1,4 @@
|
||||||
MODULE ::= INSTR*
|
MODULE ::= INSTR*
|
||||||
INSTR ::= EXPR semicolon
|
INSTR ::= EXPR semicolon
|
||||||
EXPR ::= BUILTIN
|
EXPR ::= BUILTIN
|
||||||
BUILTIN ::= bool
|
BUILTIN ::= bool | float | int
|
||||||
|
|
139
src/ast/lexer.rs
139
src/ast/lexer.rs
|
@ -1,8 +1,8 @@
|
||||||
use crate::ast::node;
|
use crate::ast::node::Node;
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
position: usize,
|
position: usize,
|
||||||
value: String,
|
value: String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Lexer {
|
pub struct Lexer {
|
||||||
|
@ -53,6 +53,79 @@ impl Lexer {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn scan_int(&self) -> Option<Token> {
|
||||||
|
let mut value = String::from("");
|
||||||
|
|
||||||
|
if let Some('-') = self.text.chars().nth(self.cursor) {
|
||||||
|
value.push('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in self.text.chars().skip(self.cursor + value.len()) {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
value.push(c);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.len() > 0 {
|
||||||
|
Some(Token {
|
||||||
|
position: self.cursor + value.len(),
|
||||||
|
value
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_float(&self) -> Option<Token> {
|
||||||
|
let mut value = String::from("");
|
||||||
|
|
||||||
|
if let Some('-') = self.text.chars().nth(self.cursor) {
|
||||||
|
value.push('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in self.text.chars().skip(self.cursor + value.len()) {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
value.push(c);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some('.') = self.text.chars().nth(self.cursor + value.len()) {
|
||||||
|
value.push('.');
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in self.text.chars().skip(self.cursor + value.len()) {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
value.push(c);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.len() > 0 && value != "." {
|
||||||
|
if value.chars().nth(0).unwrap() == '.' {
|
||||||
|
value = format!("0{}", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.chars().last().unwrap() == '.' {
|
||||||
|
value = format!("{}0", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Token {
|
||||||
|
position: self.cursor + value.len(),
|
||||||
|
value
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn scan_text(&self, text: &str) -> Option<Token> {
|
fn scan_text(&self, text: &str) -> Option<Token> {
|
||||||
let c = self.cursor;
|
let c = self.cursor;
|
||||||
|
|
||||||
|
@ -112,24 +185,38 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for Lexer {
|
impl Iterator for Lexer {
|
||||||
type Item = node::Node;
|
type Item = Node;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.skip_spaces();
|
self.skip_spaces();
|
||||||
|
|
||||||
if let Some(token) = self.scan_keyword("true") {
|
if let Some(token) = self.scan_keyword("true") {
|
||||||
self.cursor = token.position;
|
self.cursor = token.position;
|
||||||
return Some(node::Node::Bool(token.value == "true"));
|
return Some(Node::Bool(token.value == "true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(token) = self.scan_keyword("false") {
|
if let Some(token) = self.scan_keyword("false") {
|
||||||
self.cursor = token.position;
|
self.cursor = token.position;
|
||||||
return Some(node::Node::Bool(token.value == "true"));
|
return Some(Node::Bool(token.value == "true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(token) = self.scan_text(";") {
|
if let Some(token) = self.scan_text(";") {
|
||||||
self.cursor = token.position;
|
self.cursor = token.position;
|
||||||
return Some(node::Node::Semicolon);
|
return Some(Node::Semicolon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(token) = self.scan_float() {
|
||||||
|
if let Ok(val) = token.value.parse::<f64>() {
|
||||||
|
self.cursor = token.position;
|
||||||
|
return Some(Node::Float(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(token) = self.scan_int() {
|
||||||
|
if let Ok(val) = token.value.parse::<i32>() {
|
||||||
|
self.cursor = token.position;
|
||||||
|
return Some(Node::Int(val));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -140,8 +227,9 @@ impl Iterator for Lexer {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::error;
|
use crate::ast::error;
|
||||||
|
use crate::ast::node::Node;
|
||||||
|
|
||||||
fn lexer_run(lexer: &mut Lexer, text: &str) -> Result<Vec<node::Node>, error::AstError> {
|
fn lexer_run(lexer: &mut Lexer, text: &str) -> Result<Vec<Node>, error::AstError> {
|
||||||
lexer.prepare(text);
|
lexer.prepare(text);
|
||||||
let result = lexer.collect();
|
let result = lexer.collect();
|
||||||
|
|
||||||
|
@ -168,8 +256,8 @@ mod test {
|
||||||
let mut lex = Lexer::new();
|
let mut lex = Lexer::new();
|
||||||
let res = lexer_run(&mut lex, "true false").unwrap();
|
let res = lexer_run(&mut lex, "true false").unwrap();
|
||||||
assert_eq!(2, res.len());
|
assert_eq!(2, res.len());
|
||||||
assert_eq!("Bool(true)", format!("{:?}", res.get(0).unwrap()));
|
assert_eq!(Node::Bool(true), *res.get(0).unwrap());
|
||||||
assert_eq!("Bool(false)", format!("{:?}", res.get(1).unwrap()));
|
assert_eq!(Node::Bool(false), *res.get(1).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -177,8 +265,8 @@ mod test {
|
||||||
let mut lex = Lexer::new();
|
let mut lex = Lexer::new();
|
||||||
let res = lexer_run(&mut lex, "true;").unwrap();
|
let res = lexer_run(&mut lex, "true;").unwrap();
|
||||||
assert_eq!(2, res.len());
|
assert_eq!(2, res.len());
|
||||||
assert_eq!("Bool(true)", format!("{:?}", res.get(0).unwrap()));
|
assert_eq!(Node::Bool(true), *res.get(0).unwrap());
|
||||||
assert_eq!("Semicolon", format!("{:?}", res.get(1).unwrap()));
|
assert_eq!(Node::Semicolon, *res.get(1).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -203,8 +291,33 @@ mod test {
|
||||||
let mut lex = Lexer::new();
|
let mut lex = Lexer::new();
|
||||||
let res = lexer_run(&mut lex, " ;; ")?;
|
let res = lexer_run(&mut lex, " ;; ")?;
|
||||||
assert_eq!(2, res.len());
|
assert_eq!(2, res.len());
|
||||||
assert_eq!("Semicolon", format!("{:?}", res.get(0).unwrap()));
|
assert_eq!(Node::Semicolon, *res.get(0).unwrap());
|
||||||
assert_eq!("Semicolon", format!("{:?}", res.get(1).unwrap()));
|
assert_eq!(Node::Semicolon, *res.get(1).unwrap());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integers() -> Result<(), error::AstError> {
|
||||||
|
let mut lex = Lexer::new();
|
||||||
|
let res = lexer_run(&mut lex, " 4 -2 328")?;
|
||||||
|
assert_eq!(3, res.len());
|
||||||
|
assert_eq!(Node::Int(4), *res.get(0).unwrap());
|
||||||
|
assert_eq!(Node::Int(-2), *res.get(1).unwrap());
|
||||||
|
assert_eq!(Node::Int(328), *res.get(2).unwrap());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_floats() -> Result<(), error::AstError> {
|
||||||
|
let mut lex = Lexer::new();
|
||||||
|
let res = lexer_run(&mut lex, " 2.0 .7 5. -3.14 ")?;
|
||||||
|
assert_eq!(4, res.len());
|
||||||
|
assert_eq!(Node::Float(2.0), *res.get(0).unwrap());
|
||||||
|
assert_eq!(Node::Float(0.7), *res.get(1).unwrap());
|
||||||
|
assert_eq!(Node::Float(5.0), *res.get(2).unwrap());
|
||||||
|
assert_eq!(Node::Float(-3.14), *res.get(3).unwrap());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pub enum Node {
|
pub enum Node {
|
||||||
Module(String, Vec<Node>),
|
Module(String, Vec<Node>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
Int(i32),
|
||||||
|
Float(f64),
|
||||||
Semicolon
|
Semicolon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
node,
|
node::Node,
|
||||||
lexer,
|
lexer,
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@ pub struct Parser {
|
||||||
lexer: lexer::Lexer
|
lexer: lexer::Lexer
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParseResult = Result<Option<node::Node>, error::AstError>;
|
type ParseResult = Result<Option<Node>, error::AstError>;
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(module_name: &str, lex: lexer::Lexer) -> Self {
|
pub fn new(module_name: &str, lex: lexer::Lexer) -> Self {
|
||||||
|
@ -24,7 +24,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_module(&mut self) -> ParseResult {
|
fn parse_module(&mut self) -> ParseResult {
|
||||||
let mut children: Vec<node::Node> = vec![];
|
let mut children: Vec<Node> = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.parse_instr() {
|
match self.parse_instr() {
|
||||||
|
@ -41,7 +41,7 @@ impl Parser {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = node::Node::Module(self.module_name.clone(), children);
|
let n = Node::Module(self.module_name.clone(), children);
|
||||||
Ok(Some(n))
|
Ok(Some(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ impl Parser {
|
||||||
match self.parse_expr() {
|
match self.parse_expr() {
|
||||||
Ok(Some(expr)) => {
|
Ok(Some(expr)) => {
|
||||||
match self.lexer.next() {
|
match self.lexer.next() {
|
||||||
Some(node::Node::Semicolon) => Ok(Some(expr)),
|
Some(Node::Semicolon) => Ok(Some(expr)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
_ => Err(error::AstError {
|
_ => Err(error::AstError {
|
||||||
msg: String::from("missing semicolon"),
|
msg: String::from("missing semicolon"),
|
||||||
|
@ -68,8 +68,14 @@ impl Parser {
|
||||||
|
|
||||||
fn parse_builtin(&mut self) -> ParseResult {
|
fn parse_builtin(&mut self) -> ParseResult {
|
||||||
match self.lexer.next() {
|
match self.lexer.next() {
|
||||||
Some(node::Node::Bool(value))
|
Some(Node::Int(value))
|
||||||
=> Ok(Some(node::Node::Bool(value))),
|
=> Ok(Some(Node::Int(value))),
|
||||||
|
|
||||||
|
Some(Node::Float(value))
|
||||||
|
=> Ok(Some(Node::Float(value))),
|
||||||
|
|
||||||
|
Some(Node::Bool(value))
|
||||||
|
=> Ok(Some(Node::Bool(value))),
|
||||||
|
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
Err(error::AstError {
|
Err(error::AstError {
|
||||||
|
@ -86,11 +92,10 @@ impl Parser {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::node::Node;
|
|
||||||
|
|
||||||
type TestResult = Result<(), String>;
|
type TestResult = Result<(), String>;
|
||||||
|
|
||||||
fn test_parser(oracle: node::Node, source: &str) -> TestResult {
|
fn test_parser(oracle: Node, source: &str) -> TestResult {
|
||||||
let mut lex = lexer::Lexer::new();
|
let mut lex = lexer::Lexer::new();
|
||||||
lex.prepare(source);
|
lex.prepare(source);
|
||||||
let mut parser = Parser::new("mod", lex);
|
let mut parser = Parser::new("mod", lex);
|
||||||
|
@ -140,4 +145,23 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn numbers() -> TestResult {
|
||||||
|
test_parser(
|
||||||
|
Node::Module("mod".to_owned(), vec![
|
||||||
|
Node::Float(27.3)
|
||||||
|
]),
|
||||||
|
" 27.3; "
|
||||||
|
)?;
|
||||||
|
|
||||||
|
test_parser(
|
||||||
|
Node::Module("mod".to_owned(), vec![
|
||||||
|
Node::Int(-27)
|
||||||
|
]),
|
||||||
|
" -27; "
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,11 @@ impl Evaluator {
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
},
|
},
|
||||||
|
|
||||||
Node::Bool(value) => Ok(Some(Value::Bool(*value))),
|
Node::Bool(value) => Ok(Some(Value::Bool(*value))),
|
||||||
|
Node::Int(value) => Ok(Some(Value::Int(*value))),
|
||||||
|
Node::Float(value) => Ok(Some(Value::Float(*value))),
|
||||||
|
|
||||||
_ => Ok(None)
|
_ => Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +69,10 @@ mod test {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Err(Box::new(EvalError {
|
||||||
|
msg: format!("unexpected: {:?}", value),
|
||||||
|
line: 0
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Ok(())
|
None => Ok(())
|
||||||
|
@ -79,4 +86,12 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn numbers() -> TestResult {
|
||||||
|
test_eval_value(Value::Float(-271.4), " -271.4; ")?;
|
||||||
|
test_eval_value(Value::Int(333), " 333; ")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Bool(bool)
|
Bool(bool),
|
||||||
|
Int(i32),
|
||||||
|
Float(f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue