loops
This commit is contained in:
parent
01d49ff724
commit
62ff55df7a
8 changed files with 232 additions and 8 deletions
|
|
@ -30,13 +30,6 @@ pub enum Expr {
|
||||||
},
|
},
|
||||||
Record(BTreeMap<Label, Expr>),
|
Record(BTreeMap<Label, Expr>),
|
||||||
Vector(Vec<Expr>),
|
Vector(Vec<Expr>),
|
||||||
/*
|
|
||||||
Loop {
|
|
||||||
body: Box<Expr>,
|
|
||||||
},
|
|
||||||
Continue,
|
|
||||||
Break,
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
|
|
@ -80,6 +73,8 @@ pub enum Statement {
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
Let(Definition),
|
Let(Definition),
|
||||||
Return(Option<Expr>),
|
Return(Option<Expr>),
|
||||||
|
Loop(Box<Expr>),
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
|
|
@ -103,6 +98,7 @@ pub enum Value {
|
||||||
Closure { env: EnvName, expr: Box<Expr> },
|
Closure { env: EnvName, expr: Box<Expr> },
|
||||||
PrimitiveFunc(Name),
|
PrimitiveFunc(Name),
|
||||||
Return(Box<Value>),
|
Return(Box<Value>),
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutable variable.
|
/// A mutable variable.
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ pub fn run(
|
||||||
enum StatementResult {
|
enum StatementResult {
|
||||||
Result(ast::Value),
|
Result(ast::Value),
|
||||||
Return(ast::Value),
|
Return(ast::Value),
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_statement(
|
fn eval_statement(
|
||||||
|
|
@ -56,7 +57,11 @@ fn eval_statement(
|
||||||
statement: &ast::Statement,
|
statement: &ast::Statement,
|
||||||
) -> Result<StatementResult, Error> {
|
) -> Result<StatementResult, Error> {
|
||||||
match statement {
|
match statement {
|
||||||
ast::Statement::Expr(expr) => eval_expr(expr_env, state, expr).map(StatementResult::Result),
|
ast::Statement::Expr(expr) => match eval_expr(expr_env, state, expr)? {
|
||||||
|
ast::Value::Break => Ok(StatementResult::Break),
|
||||||
|
ast::Value::Return(v) => Ok(StatementResult::Return(*v)),
|
||||||
|
v => Ok(StatementResult::Result(v)),
|
||||||
|
},
|
||||||
ast::Statement::Let(ast::Definition {
|
ast::Statement::Let(ast::Definition {
|
||||||
mutable,
|
mutable,
|
||||||
name,
|
name,
|
||||||
|
|
@ -74,6 +79,18 @@ fn eval_statement(
|
||||||
Some(expr) => eval_expr(expr_env, state, expr).map(StatementResult::Return),
|
Some(expr) => eval_expr(expr_env, state, expr).map(StatementResult::Return),
|
||||||
None => eval_expr(expr_env, state, &ast::UNIT).map(StatementResult::Return),
|
None => eval_expr(expr_env, state, &ast::UNIT).map(StatementResult::Return),
|
||||||
},
|
},
|
||||||
|
ast::Statement::Break => Ok(StatementResult::Break),
|
||||||
|
ast::Statement::Loop(expr) => {
|
||||||
|
loop {
|
||||||
|
match eval_expr(expr_env, state, expr)? {
|
||||||
|
ast::Value::Break => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(StatementResult::Result(ast::UNIT_VALUE))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +123,10 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast:
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
let r = eval_statement(&mut block_env, state, statement)?;
|
let r = eval_statement(&mut block_env, state, statement)?;
|
||||||
match r {
|
match r {
|
||||||
|
StatementResult::Break => {
|
||||||
|
result = ast::Value::Break;
|
||||||
|
break;
|
||||||
|
}
|
||||||
StatementResult::Result(ast::Value::Return(r)) => {
|
StatementResult::Result(ast::Value::Return(r)) => {
|
||||||
result = ast::Value::Return(r);
|
result = ast::Value::Return(r);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
22
src/lib.rs
22
src/lib.rs
|
|
@ -68,6 +68,28 @@ let main = fn() {
|
||||||
insta::assert_debug_snapshot!(result);
|
insta::assert_debug_snapshot!(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loop_counter_9() {
|
||||||
|
let program = "
|
||||||
|
let counter = fn(a,b) {
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
if (a + count) >= b {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
count = count + 1;
|
||||||
|
};
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
let main = fn() {
|
||||||
|
counter(1,10)
|
||||||
|
}
|
||||||
|
";
|
||||||
|
let result = run_main(program);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn direction() {
|
fn direction() {
|
||||||
let program = "
|
let program = "
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,30 @@ fn parse_statement(tokens: &mut Tokens) -> ParseResult<ast::Statement> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|tokens| {
|
||||||
|
if let Some(_) = tokens.next_if(&Token::Break) {
|
||||||
|
Ok(Some(ast::Statement::Break))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|t| {
|
||||||
|
if let Some(_) = t.next_if(&Token::Loop) {
|
||||||
|
if let Some(block) = t
|
||||||
|
.between(&Token::OpenCurly, &Token::CloseCurly, parse_block)?
|
||||||
|
.map(ast::Expr::Block)
|
||||||
|
{
|
||||||
|
Ok(Some(ast::Statement::Loop(Box::new(block))))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnexpectedTokenForParser(
|
||||||
|
"loop expression".into(),
|
||||||
|
t.next(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -515,6 +539,24 @@ mod tests {
|
||||||
insta::assert_debug_snapshot!(result);
|
insta::assert_debug_snapshot!(result);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn let_loop_count() {
|
||||||
|
let program = "
|
||||||
|
let counter = fn(a,b) {
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
if (a + count) >= b {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
count = count + 1;
|
||||||
|
};
|
||||||
|
count
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.to_string();
|
||||||
|
let result = parse(&mut scan(program), parse_program);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
fn full_program() {
|
fn full_program() {
|
||||||
let program = "
|
let program = "
|
||||||
let setup = fn() {
|
let setup = fn() {
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,8 @@ pub fn scan(source: String) -> Tokens {
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
"else" => Token::Else,
|
"else" => Token::Else,
|
||||||
"return" => Token::Return,
|
"return" => Token::Return,
|
||||||
|
"loop" => Token::Loop,
|
||||||
|
"break" => Token::Break,
|
||||||
"true" => Token::True,
|
"true" => Token::True,
|
||||||
"false" => Token::False,
|
"false" => Token::False,
|
||||||
_ => Token::Identifier(str),
|
_ => Token::Identifier(str),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
---
|
||||||
|
source: src/parser/parser.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Program(
|
||||||
|
[
|
||||||
|
Definition {
|
||||||
|
mutable: false,
|
||||||
|
name: Name(
|
||||||
|
"counter",
|
||||||
|
),
|
||||||
|
expr: Func(
|
||||||
|
Fn {
|
||||||
|
args: [
|
||||||
|
Arg {
|
||||||
|
name: Name(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Arg {
|
||||||
|
name: Name(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: Block(
|
||||||
|
[
|
||||||
|
Let(
|
||||||
|
Definition {
|
||||||
|
mutable: true,
|
||||||
|
name: Name(
|
||||||
|
"count",
|
||||||
|
),
|
||||||
|
expr: Value(
|
||||||
|
Int(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Loop(
|
||||||
|
Block(
|
||||||
|
[
|
||||||
|
Expr(
|
||||||
|
If {
|
||||||
|
condition: Op {
|
||||||
|
lhs: Op {
|
||||||
|
lhs: Var(
|
||||||
|
Name(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
op: Calc(
|
||||||
|
Add,
|
||||||
|
),
|
||||||
|
rhs: Var(
|
||||||
|
Name(
|
||||||
|
"count",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
op: Compare(
|
||||||
|
Gte,
|
||||||
|
),
|
||||||
|
rhs: Var(
|
||||||
|
Name(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
then: Block(
|
||||||
|
[
|
||||||
|
Break,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
else: Value(
|
||||||
|
Record(
|
||||||
|
Record(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expr(
|
||||||
|
Op {
|
||||||
|
lhs: Var(
|
||||||
|
Name(
|
||||||
|
"count",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
op: Assign,
|
||||||
|
rhs: Op {
|
||||||
|
lhs: Var(
|
||||||
|
Name(
|
||||||
|
"count",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
op: Calc(
|
||||||
|
Add,
|
||||||
|
),
|
||||||
|
rhs: Value(
|
||||||
|
Int(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expr(
|
||||||
|
Var(
|
||||||
|
Name(
|
||||||
|
"count",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -37,6 +37,8 @@ pub enum Token {
|
||||||
False,
|
False,
|
||||||
Equals,
|
Equals,
|
||||||
Not,
|
Not,
|
||||||
|
Loop,
|
||||||
|
Break,
|
||||||
Op(Op),
|
Op(Op),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f32),
|
Float(f32),
|
||||||
|
|
|
||||||
11
src/snapshots/ayin__tests__loop_counter_9.snap
Normal file
11
src/snapshots/ayin__tests__loop_counter_9.snap
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Value(
|
||||||
|
Int(
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
Loading…
Add table
Reference in a new issue