Added tests and loop flow
This commit is contained in:
parent
ecce080378
commit
051f6e781f
|
@ -94,6 +94,12 @@ pub enum Token {
|
|||
#[token("loop")]
|
||||
Loop,
|
||||
|
||||
#[token("break")]
|
||||
Break,
|
||||
|
||||
#[token("hopback")]
|
||||
HopBack,
|
||||
|
||||
// Literals
|
||||
/// True, False
|
||||
#[regex("true|false", get_bool)]
|
||||
|
@ -115,6 +121,9 @@ pub enum Token {
|
|||
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
||||
Identifier(String),
|
||||
|
||||
#[regex("nul")]
|
||||
Nul,
|
||||
|
||||
#[token("(")]
|
||||
LeftParenthesis,
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::variables::Value;
|
|||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Iden(pub String);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Item {
|
||||
Expr(Expr),
|
||||
Stmt(Stmt),
|
||||
|
@ -15,13 +15,19 @@ impl From<Expr> for Item {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Iden> for Item {
|
||||
fn from(i: Iden) -> Self {
|
||||
Item::Expr(Expr::Identifier(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Stmt> for Item {
|
||||
fn from(s: Stmt) -> Self {
|
||||
Item::Stmt(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expr {
|
||||
Add { left: Box<Expr>, right: Box<Expr> },
|
||||
Subtract { left: Box<Expr>, right: Box<Expr> },
|
||||
|
@ -43,7 +49,7 @@ impl From<Iden> for Expr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Stmt {
|
||||
VariableDeclaration {
|
||||
iden: Iden,
|
||||
|
@ -66,6 +72,11 @@ pub enum Stmt {
|
|||
iden: Iden,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
Loop {
|
||||
body: Vec<Item>,
|
||||
},
|
||||
Break,
|
||||
HopBack,
|
||||
Print(Expr),
|
||||
Melo(Iden),
|
||||
}
|
||||
|
|
|
@ -67,10 +67,21 @@ impl<'a> Parser<'a> {
|
|||
| Token::Boolean(_)
|
||||
| Token::Integer(_)
|
||||
| Token::String(_)
|
||||
| Token::Nul
|
||||
| Token::LogNot => self.parse_ops(token),
|
||||
|
||||
// Control flow
|
||||
Token::If => self.if_cond(),
|
||||
Token::Loop => self.loop_block(),
|
||||
|
||||
Token::HopBack => {
|
||||
self.require(Token::Semicolon)?;
|
||||
Ok(Stmt::HopBack.into())
|
||||
}
|
||||
Token::Break => {
|
||||
self.require(Token::Semicolon)?;
|
||||
Ok(Stmt::Break.into())
|
||||
}
|
||||
|
||||
// Declarations
|
||||
Token::Variable => self.variable_declaration(),
|
||||
|
@ -194,6 +205,14 @@ impl<'a> Parser<'a> {
|
|||
.into())
|
||||
}
|
||||
|
||||
/// Parse loop
|
||||
pub fn loop_block(&mut self) -> ParseResult {
|
||||
self.require(Token::LeftBrace)?;
|
||||
let body = self.parse_body()?;
|
||||
|
||||
Ok(Stmt::Loop { body }.into())
|
||||
}
|
||||
|
||||
/// T-Dark block parsing
|
||||
pub fn tdark_block(&mut self) -> Result<Vec<Item>, Error> {
|
||||
self.require(Token::LeftBrace)?;
|
||||
|
@ -221,3 +240,62 @@ impl<'a> Parser<'a> {
|
|||
Ok(body)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use Expr::*;
|
||||
use Stmt::*;
|
||||
|
||||
#[test]
|
||||
fn control_flow() {
|
||||
let code = r#"loop { var a = 3 + 2; if (a == 5) { break; } }"#;
|
||||
|
||||
let expected: &[Item] = &[Item::Stmt(Loop {
|
||||
body: vec![
|
||||
VariableDeclaration {
|
||||
iden: Iden("a".to_owned()),
|
||||
init: Some(Box::new(
|
||||
Add {
|
||||
left: Box::new(Literal(Value::Int(3))),
|
||||
right: Box::new(Literal(Value::Int(2))),
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
}
|
||||
.into(),
|
||||
If {
|
||||
cond: Box::new(
|
||||
Eq {
|
||||
left: Box::new(Iden("a".to_owned()).into()),
|
||||
right: Box::new(Literal(Value::Int(5)).into()),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
body: vec![Break.into()],
|
||||
}
|
||||
.into(),
|
||||
],
|
||||
})];
|
||||
let ast = Parser::new(code).init().unwrap();
|
||||
|
||||
assert_eq!(ast, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tdark() {
|
||||
let code = r#"T-Dark { var lang = nul; lang print; }"#;
|
||||
let expected: &[Item] = &[
|
||||
VariableDeclaration {
|
||||
iden: Iden("script".to_owned()),
|
||||
init: Some(Box::new(Literal(Value::Nul).into())),
|
||||
}
|
||||
.into(),
|
||||
Print(Iden("script".to_owned()).into()).into(),
|
||||
];
|
||||
|
||||
let ast = Parser::new(code).init().unwrap();
|
||||
|
||||
assert_eq!(ast, expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
i
|
||||
}))),
|
||||
Token::Nul => Ok(Expr::Literal(Value::Nul)),
|
||||
Token::LogNot => {
|
||||
let next = self.lexer.next();
|
||||
Ok(Expr::Not(Box::new(self.parse_expr(next)?)))
|
||||
|
|
|
@ -17,7 +17,7 @@ impl From<Abool> for bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
Str(String),
|
||||
Int(i32),
|
||||
|
|
Loading…
Reference in a new issue