use logos::{Lexer, Logos}; use crate::ParseResult; #[derive(Logos, Debug, PartialEq, Copy, Clone)] pub enum C1Token { #[token("bool")] KwBoolean, #[token("do")] KwDo, #[token("else")] KwElse, #[token("float")] KwFloat, #[token("for")] KwFor, #[token("if")] KwIf, #[token("int")] KwInt, #[token("printf")] KwPrintf, #[token("return")] KwReturn, #[token("void")] KwVoid, #[token("while")] KwWhile, #[token("+")] Plus, #[token("-")] Minus, #[token("*")] Asterisk, #[token("/")] Slash, #[token("=")] /// = Assign, #[token("==")] /// == Equal, #[token("!=")] /// != NotEqual, #[token("<")] /// < Less, #[token(">")] /// > Greater, #[token("<=")] /// <= LessEqual, #[token(">=")] /// >= GreaterEqual, #[token("&&")] /// && And, #[token("||")] /// || Or, #[token(",")] Comma, #[token(";")] Semicolon, #[token("(")] /// ( LeftParenthesis, #[token(")")] /// ) RightParenthesis, #[token("{")] /// { LeftBrace, #[token("}")] /// } RightBrace, #[regex("[0-9]+")] ConstInt, #[regex(r"(\d+\.\d+)|(\.\d+([eE]([-+])?\d+)?)|(\d+[eE]([-+])?\d+)")] ConstFloat, #[regex("true|false")] ConstBoolean, #[regex("\"[^\n\"]*\"")] ConstString, #[regex("[a-zA-Z]+[0-9a-zA-Z]*")] Identifier, #[regex(r"/\*[^\*/]*\*/", logos::skip)] CComment, #[regex("//[^\n]*(\n)?", logos::skip)] CPPComment, // We can also use this variant to define whitespace, // or any other matches we wish to skip. #[regex(r"[ \t\f]+", logos::skip)] Whitespace, #[regex(r"[\n]")] Linebreak, // Logos requires one token variant to handle errors, // it can be named anything you wish. #[error] Error, } /// # Overview /// Extended lexer based on the logos crate. The lexer keeps track of the current token and the next token /// in the lexed text. Furthermore, the lexer keeps track of the line number in which each token is /// located, and of the text associated with each token. /// /// # Examples /// ``` /// use cb_3::C1Lexer; /// use cb_3::C1Token; /// /// let mut lexer = C1Lexer::new("void main() { /// x = 4; /// }"); /// assert_eq!(lexer.current_token(), Some(C1Token::KwVoid)); /// assert_eq!(lexer.current_line_number(), Some(1)); /// assert_eq!(lexer.peek_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.peek_line_number(), Some(1)); /// /// lexer.eat(); /// // current token is 'main' /// /// lexer.eat(); /// lexer.eat(); /// lexer.eat(); /// // current token is '{' /// /// assert_eq!(lexer.current_token(), Some(C1Token::LeftBrace)); /// assert_eq!(lexer.current_line_number(), Some(1)); /// /// // next token is 'x' /// assert_eq!(lexer.peek_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.peek_text(), Some("x")); /// assert_eq!(lexer.peek_line_number(), Some(2)); /// ``` pub struct C1Lexer<'a> { logos_lexer: Lexer<'a, C1Token>, logos_line_number: usize, current_token: Option<TokenData<'a>>, peek_token: Option<TokenData<'a>>, } impl<'a> C1Lexer<'a> { /// Initialize a new C1Lexer for the given string slice pub fn new(text: &'a str) -> C1Lexer { let mut lexer = C1Lexer { logos_lexer: C1Token::lexer(text), logos_line_number: 1, current_token: None, peek_token: None, }; lexer.current_token = lexer.next_token(); lexer.peek_token = lexer.next_token(); lexer } /// Return the C1Token variant of the current token without consuming it. /// ``` /// use cb_3::{C1Lexer, C1Token}; /// let lexer = C1Lexer::new("current next"); /// /// assert_eq!(lexer.current_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.current_text(), Some("current")); /// /// assert_eq!(lexer.current_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.current_text(), Some("current")); /// ``` pub fn current_token(&self) -> Option<C1Token> { self.current_token.token_type() } /// Return the C1Token variant of the next token without consuming it. ///``` /// use cb_3::{C1Lexer, C1Token}; /// let lexer = C1Lexer::new("current next"); /// /// assert_eq!(lexer.peek_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.peek_text(), Some("next")); /// /// assert_eq!(lexer.peek_token(), Some(C1Token::Identifier)); /// assert_eq!(lexer.peek_text(), Some("next")); /// ``` pub fn peek_token(&self) -> Option<C1Token> { self.peek_token.token_type() } /// Return the text of the current token pub fn current_text(&self) -> Option<&str> { self.current_token.text() } /// Return the text of the next token pub fn peek_text(&self) -> Option<&str> { self.peek_token.text() } /// Return the line number where the current token is located pub fn current_line_number(&self) -> Option<usize> { self.current_token.line_number() } /// Return the line number where the next token is located pub fn peek_line_number(&self) -> Option<usize> { self.peek_token.line_number() } /// Drop the current token and retrieve the next token in the text. /// ``` /// use cb_3::{C1Lexer, C1Token}; /// let mut lexer = C1Lexer::new("current next last"); /// /// assert_eq!(lexer.current_text(), Some("current")); /// assert_eq!(lexer.peek_text(), Some("next")); /// /// lexer.eat(); /// assert_eq!(lexer.current_text(), Some("next")); /// assert_eq!(lexer.peek_text(), Some("last")); /// /// lexer.eat(); /// assert_eq!(lexer.current_text(), Some("last")); /// assert_eq!(lexer.peek_text(), None); /// /// lexer.eat(); /// assert_eq!(lexer.current_text(), None); /// assert_eq!(lexer.peek_text(), None); /// ``` pub fn eat(&mut self) { self.current_token = self.peek_token.take(); self.peek_token = self.next_token(); } /// Private method for reading the next token from the logos::Lexer and extracting the required data /// from it fn next_token(&mut self) -> Option<TokenData<'a>> { // Retrieve the next token from the internal lexer if let Some(c1_token) = self.logos_lexer.next() { match c1_token { C1Token::Linebreak => { // If the token is a linebreak, increase the line number and get the next token self.logos_line_number += 1; self.next_token() } _ => Some(TokenData { // If the token is not a linebreak, initialize and return a TokenData instance token_type: c1_token, token_text: self.logos_lexer.slice(), token_line: self.logos_line_number, }), } } else { None } } } /// Hidden struct for capsuling the data associated with a token. struct TokenData<'a> { token_type: C1Token, token_text: &'a str, token_line: usize, } /// Hidden trait that makes it possible to implemented the required getter functionality directly for /// Option<TokenData>. trait TokenDataProvider<'a> { /// Return the type of the token, aka. its C1Token variant. fn token_type(&self) -> Option<C1Token>; /// Return the text of the token fn text(&self) -> Option<&str>; /// Return the line number of the token fn line_number(&self) -> Option<usize>; } impl<'a> TokenDataProvider<'a> for Option<TokenData<'a>> { fn token_type(&self) -> Option<C1Token> { self.as_ref().map(|data| data.token_type) } fn text(&self) -> Option<&'a str> { self.as_ref().map(|data| data.token_text) } fn line_number(&self) -> Option<usize> { self.as_ref().map(|data| data.token_line) } } pub struct C1Parser<'a>{ lexer: C1Lexer<'a>, result: ParseResult, } impl<'a> C1Parser<'a>{ pub fn parse(text: &'a str) -> ParseResult{ let mut parser = C1Parser::new(text); parser.result = parser.programm(); parser.result } pub fn new(text: &'a str) -> C1Parser { let parser = C1Parser { lexer: C1Lexer::new(text), result: Ok(()), }; parser } fn programm(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(..) => { self.result = self.functiondefinition(); self.result = self.programm();}, None => {}, }; } self.result.clone() } fn functiondefinition(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::KwBoolean) => { self.result = self.r#type(); self.result = self.chek_and_eat(C1Token::Identifier); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.chek_and_eat(C1Token::RightParenthesis); self.result = self.chek_and_eat(C1Token::LeftBrace); self.result = self.statementlist(); self.result = self.chek_and_eat(C1Token::RightBrace); }, Some(C1Token::KwFloat) => { self.result = self.r#type(); self.result = self.chek_and_eat(C1Token::Identifier); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.chek_and_eat(C1Token::RightParenthesis); self.result = self.chek_and_eat(C1Token::LeftBrace); self.result = self.statementlist(); self.result = self.chek_and_eat(C1Token::RightBrace); }, Some(C1Token::KwInt) => { self.result = self.r#type(); self.result = self.chek_and_eat(C1Token::Identifier); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.chek_and_eat(C1Token::RightParenthesis); self.result = self.chek_and_eat(C1Token::LeftBrace); self.result = self.statementlist(); self.result = self.chek_and_eat(C1Token::RightBrace); }, Some(C1Token::KwVoid) => { self.result = self.r#type(); self.result = self.chek_and_eat(C1Token::Identifier); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.chek_and_eat(C1Token::RightParenthesis); self.result = self.chek_and_eat(C1Token::LeftBrace); self.result = self.statementlist(); self.result = self.chek_and_eat(C1Token::RightBrace); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted boolean, float, int or void got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn functioncall(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Identifier) => { self.eat(); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.chek_and_eat(C1Token::LeftParenthesis); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted Identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn statementlist(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::LeftBrace) => { self.result = self.block(); self.result = self.statementlist() }, Some(C1Token::KwIf) => { self.result = self.block(); self.result = self.statementlist() }, Some(C1Token::KwReturn) => { self.result = self.block(); self.result = self.statementlist() }, Some(C1Token::KwPrintf) => { self.result = self.block(); self.result = self.statementlist() }, Some(C1Token::Identifier) => { self.result = self.block(); self.result = self.statementlist() }, // TODO: hier fehlt noch die follows menge statementlist ist epsilon ableitbar // vermutlich eifach im other nicht teun und keine fehlerbehandlung // dann die Fehlerbehandlung der nächsten höheren funktion überlassen _other => { //let linenumber = self.lexer.current_line_number().unwrap().to_string(); //self.result = Result::Err("expekted {, if, return, Print or identifier got: ".to_string() + self.lexer.current_text().unwrap() + &"at line".to_string() + &linenumber) }, // TODO: Fehlerbehandlung hinzufügen }; } self.result.clone() } fn block(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::LeftBrace) => { self.eat(); self.result = self.statementlist(); self.result = self.chek_and_eat(C1Token::RightBrace); }, Some(C1Token::KwIf) => self.result = self.statement(), Some(C1Token::KwReturn) => self.result = self.statement(), Some(C1Token::KwPrintf) => self.result = self.statement(), Some(C1Token::Identifier) => self.result = self.statement(), _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted {, if, return, Print or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn statement(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::KwIf) => { self.result = self.ifstatement(); self.result = self.chek_and_eat(C1Token::Semicolon); }, Some(C1Token::KwReturn) => { self.result = self.returnstatement(); self.result = self.chek_and_eat(C1Token::Semicolon); }, Some(C1Token::KwPrintf) => { self.result = self.printf(); self.result = self.chek_and_eat(C1Token::Semicolon); }, Some(C1Token::Identifier) => {match self.lexer.peek_token(){ Some(C1Token::Assign) => { self.result = self.statassignment(); self.result = self.chek_and_eat(C1Token::Semicolon); }, Some(C1Token::LeftParenthesis) => { self.result = self.functioncall(); self.result = self.chek_and_eat(C1Token::Semicolon); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted == or ( got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted if, return, Print or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn ifstatement(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::KwIf) => {self.eat(); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.assignment(); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.block(); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted if got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn returnstatement(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::KwReturn) => {match self.lexer.peek_token() { Some(C1Token::Minus) => self.result = self.assignment(), Some(C1Token::ConstInt) => self.result = self.assignment(), Some(C1Token::ConstFloat) => self.result = self.assignment(), Some(C1Token::ConstBoolean) => self.result = self.assignment(), Some(C1Token::LeftParenthesis) => self.result = self.assignment(), Some(C1Token::Identifier) => self.result = self.assignment(), Some(C1Token::Semicolon) => {}, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted -, int, float, boolean, (, identifier or ; got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, };}, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted return got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn printf(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::KwPrintf) => { self.eat(); self.result = self.chek_and_eat(C1Token::LeftParenthesis); self.result = self.assignment(); self.result = self.chek_and_eat(C1Token::RightParenthesis); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted print got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn r#type(&mut self) -> ParseResult{ if self.result == Ok(()){ match &self.lexer.current_token() { Some(C1Token::KwBoolean) => self.eat(), Some(C1Token::KwFloat) => self.eat(), Some(C1Token::KwInt) => self.eat(), Some(C1Token::KwVoid) => self.eat(), _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted Boolean, float, int or void got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn statassignment(&mut self) -> ParseResult{ if self.result == Ok(()){ match&self.lexer.current_token() { Some(C1Token::Identifier) => { self.eat(); self.result = self.chek_and_eat(C1Token::Assign); self.result = self.assignment(); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn assignment(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Identifier) => { match self.lexer.peek_token(){ Some(C1Token::Assign) => { self.eat(); self.eat(); self.result = self.assignment(); } _other => self.result = self.expr(), }; }, Some(C1Token::Minus) => self.result = self.expr(), Some(C1Token::ConstInt) => self.result = self.expr(), Some(C1Token::ConstFloat) => self.result = self.expr(), Some(C1Token::ConstBoolean) => self.result = self.expr(), Some(C1Token::LeftParenthesis) => self.result = self.expr(), _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted identifier, -, int, float, Boolean or ( got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, } } self.result.clone() } fn expr(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Minus) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, Some(C1Token::ConstInt) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, Some(C1Token::ConstFloat) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, Some(C1Token::ConstBoolean) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, Some(C1Token::LeftParenthesis) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, Some(C1Token::Identifier) => { self.result = self.simpexpr(); self.result = self.helpexpr(); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted -, int, float, boolean, ( or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn helpexpr(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Equal) => {self.eat(); self.result = self.simpexpr();}, Some(C1Token::NotEqual) => {self.eat(); self.result = self.simpexpr();}, Some(C1Token::LessEqual) => {self.eat(); self.result = self.simpexpr();}, Some(C1Token::GreaterEqual) => {self.eat(); self.result = self.simpexpr();}, Some(C1Token::Less) => {self.eat(); self.result = self.simpexpr();}, Some(C1Token::Greater) => {self.eat(); self.result = self.simpexpr();}, // TODO: hier fehlt noch die follows menge helpexpr ist epsilon ableitbar // vermutlich eifach im other nicht teun und keine fehlerbehandlung // dann die Fehlerbehandlung der nächsten höheren funktion überlassen _other => {}, // TODO: Fehlerbehanddlung hinzufügen }; } self.result.clone() } fn simpexpr(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Minus) => { self.eat(); self.result = self.term(); self.result = self.helpsimpexpr(); }, Some(C1Token::ConstInt) => { self.result = self.term(); self.result = self.helpsimpexpr() }, Some(C1Token::ConstFloat) => { self.result = self.term(); self.result = self.helpsimpexpr() }, Some(C1Token::ConstBoolean) => { self.result = self.term(); self.result = self.helpsimpexpr() }, Some(C1Token::LeftParenthesis) => { self.result = self.term(); self.result = self.helpsimpexpr() }, Some(C1Token::Identifier) => { self.result = self.term(); self.result = self.helpsimpexpr() }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted -, int, float, boolen, ( or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn helpsimpexpr(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Plus) => { self.eat(); self.result = self.term(); self.result = self.helpsimpexpr(); }, Some(C1Token::Minus) => { self.eat(); self.result = self.term(); self.result = self.helpsimpexpr(); }, Some(C1Token::Or) => { self.eat(); self.result = self.term(); self.result = self.helpsimpexpr(); }, // TODO: hier fehlt noch die follows menge helpsimpexpr ist epsilon ableitbar // vermutlich eifach im other nicht teun und keine fehlerbehandlung // dann die Fehlerbehandlung der nächsten höheren funktion überlassen _other => {}, // TODO: Fehlerbehandlung hinzufügen }; } self.result.clone() } fn term(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::ConstInt) => { self.result = self.factor(); self.result = self.helpterm(); }, Some(C1Token::ConstFloat) => { self.result = self.factor(); self.result = self.helpterm(); }, Some(C1Token::ConstBoolean) => { self.result = self.factor(); self.result = self.helpterm(); }, Some(C1Token::LeftParenthesis) => { self.result = self.factor(); self. result = self.helpterm(); } Some(C1Token::Identifier) => { self.result = self.factor(); self.result = self.helpterm(); }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted int, float, boolean, ( or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn helpterm(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::Asterisk) => { self.eat(); self.result = self.factor(); self.result = self.helpterm(); }, Some(C1Token::Slash) => { self.eat(); self.result = self.factor(); self.result = self.helpterm(); }, Some(C1Token::And) => { self.eat(); self.result = self.factor(); self.result = self.helpterm(); }, // TODO: hier fehlt noch die follows menge helpterm ist epsilon ableitbar // vermutlich eifach im other nicht teun und keine fehlerbehandlung // dann die Fehlerbehandlung der nächsten höheren funktion überlassen _other => {}, // TODO: fehlerbehandlung hinzufügen }; } self.result.clone() } fn factor(&mut self) -> ParseResult{ if self.result == Ok(()){ match self.lexer.current_token() { Some(C1Token::ConstInt) => self.eat(), Some(C1Token::ConstFloat) => self.eat(), Some(C1Token::ConstBoolean) => self.eat(), Some(C1Token::LeftParenthesis) => { self.eat(); self.result = self.assignment(); self.result = self.chek_and_eat(C1Token::LeftParenthesis); }, Some(C1Token::Identifier) => { match self.lexer.peek_token() { Some(C1Token::LeftParenthesis) => self.result = self.functioncall(), _other => self.eat(), }; }, _other => { let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted int, float, boolean, ( or identifier got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber) }, }; } self.result.clone() } fn eat(&mut self) { self.lexer.eat(); } fn chek_and_eat(&mut self, token: C1Token) -> ParseResult{ if self.result == Ok(()) { match self.lexer.current_token(){ Some(value) => {if value == token { self.eat() }else{ let linenumber = self.lexer.current_line_number().unwrap().to_string(); self.result = Result::Err("expekted another token got: ".to_string() + self.lexer.current_text().unwrap() + "at line" + &linenumber); } }, None => { self.result = Result::Err("there ist no more token to eat".to_string()) }, }; } self.result.clone() } } #[cfg(test)] mod tests { use crate::lexer::C1Lexer; use crate::C1Token; #[test] fn lines_are_counted() { let mut lexer1 = C1Lexer::new("Hello\nTest"); assert_eq!(lexer1.current_line_number(), Some(1)); assert_eq!(lexer1.peek_line_number(), Some(2)); lexer1.eat(); assert_eq!(lexer1.current_line_number(), Some(2)); assert_eq!(lexer1.peek_line_number(), None); lexer1.eat(); assert_eq!(lexer1.current_line_number(), None); assert_eq!(lexer1.peek_line_number(), None); } #[test] fn line_count_is_reset() { { let mut lexer1 = C1Lexer::new("Hello\nTest\nbla\nfoo"); lexer1.eat(); lexer1.eat(); assert_eq!(lexer1.current_line_number(), Some(3)); assert_eq!(lexer1.peek_line_number(), Some(4)); } let lexer2 = C1Lexer::new("bool foo()"); assert_eq!(lexer2.current_line_number(), Some(1)); assert_eq!(lexer2.peek_line_number(), Some(1)); } #[test] fn float_recognition() { let lexer = C1Lexer::new("1.2"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("1.000"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new(".2"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("1.2e4"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("1.2e+4"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("1.2e-10"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("1.2E-10"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); let lexer = C1Lexer::new("33E+2"); assert_eq!(lexer.current_token(), Some(C1Token::ConstFloat)); } }