Added tests and loop flow
This commit is contained in:
parent
f8db60bc7c
commit
d80b5514ce
|
@ -94,6 +94,12 @@ pub enum Token {
|
||||||
#[token("loop")]
|
#[token("loop")]
|
||||||
Loop,
|
Loop,
|
||||||
|
|
||||||
|
#[token("break")]
|
||||||
|
Break,
|
||||||
|
|
||||||
|
#[token("hopback")]
|
||||||
|
HopBack,
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
/// True, False
|
/// True, False
|
||||||
#[regex("true|false", get_bool)]
|
#[regex("true|false", get_bool)]
|
||||||
|
@ -115,6 +121,9 @@ pub enum Token {
|
||||||
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
|
||||||
|
#[regex("nul")]
|
||||||
|
Nul,
|
||||||
|
|
||||||
#[token("(")]
|
#[token("(")]
|
||||||
LeftParenthesis,
|
LeftParenthesis,
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::variables::Value;
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Iden(pub String);
|
pub struct Iden(pub String);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Item {
|
pub enum Item {
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
Stmt(Stmt),
|
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 {
|
impl From<Stmt> for Item {
|
||||||
fn from(s: Stmt) -> Self {
|
fn from(s: Stmt) -> Self {
|
||||||
Item::Stmt(s)
|
Item::Stmt(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Add { left: Box<Expr>, right: Box<Expr> },
|
Add { left: Box<Expr>, right: Box<Expr> },
|
||||||
Subtract { 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 {
|
pub enum Stmt {
|
||||||
VariableDeclaration {
|
VariableDeclaration {
|
||||||
iden: Iden,
|
iden: Iden,
|
||||||
|
@ -66,6 +72,11 @@ pub enum Stmt {
|
||||||
iden: Iden,
|
iden: Iden,
|
||||||
args: Vec<Expr>,
|
args: Vec<Expr>,
|
||||||
},
|
},
|
||||||
|
Loop {
|
||||||
|
body: Vec<Item>,
|
||||||
|
},
|
||||||
|
Break,
|
||||||
|
HopBack,
|
||||||
Print(Expr),
|
Print(Expr),
|
||||||
Melo(Iden),
|
Melo(Iden),
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,10 +67,21 @@ impl<'a> Parser<'a> {
|
||||||
| Token::Boolean(_)
|
| Token::Boolean(_)
|
||||||
| Token::Integer(_)
|
| Token::Integer(_)
|
||||||
| Token::String(_)
|
| Token::String(_)
|
||||||
|
| Token::Nul
|
||||||
| Token::LogNot => self.parse_ops(token),
|
| Token::LogNot => self.parse_ops(token),
|
||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
Token::If => self.if_cond(),
|
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
|
// Declarations
|
||||||
Token::Variable => self.variable_declaration(),
|
Token::Variable => self.variable_declaration(),
|
||||||
|
@ -194,6 +205,14 @@ impl<'a> Parser<'a> {
|
||||||
.into())
|
.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
|
/// T-Dark block parsing
|
||||||
pub fn tdark_block(&mut self) -> Result<Vec<Item>, Error> {
|
pub fn tdark_block(&mut self) -> Result<Vec<Item>, Error> {
|
||||||
self.require(Token::LeftBrace)?;
|
self.require(Token::LeftBrace)?;
|
||||||
|
@ -221,3 +240,62 @@ impl<'a> Parser<'a> {
|
||||||
Ok(body)
|
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 {
|
} else {
|
||||||
i
|
i
|
||||||
}))),
|
}))),
|
||||||
|
Token::Nul => Ok(Expr::Literal(Value::Nul)),
|
||||||
Token::LogNot => {
|
Token::LogNot => {
|
||||||
let next = self.lexer.next();
|
let next = self.lexer.next();
|
||||||
Ok(Expr::Not(Box::new(self.parse_expr(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 {
|
pub enum Value {
|
||||||
Str(String),
|
Str(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
|
|
Loading…
Reference in a new issue