feat(break-keyword): Break keyword impl + tests + pre-commit

egui
flavien 2022-12-03 17:51:46 +01:00
parent 9aa7619638
commit a41eba361d
21 changed files with 528 additions and 108 deletions

15
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,15 @@
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
- id: cargo-check
- id: clippy
- repo: local
hooks:
- id: tests
name: tests
entry: cargo test
language: system
pass_filenames: false
files: ^src/|^test_programs/

View File

@ -20,7 +20,8 @@ if (x) {
y = 5;
while (x) {
if (!y && z % 2) {
x = false;
z = 8;
break;
}
y = y - 1;
}
@ -31,18 +32,17 @@ if (x) {
```
Final state of the program environment:
```
var: z => val: 15
var: y => val: -1
var: x => val: false
var: y => val: 0
var: x => val: true
var: z => val: 8
```
<br>
## TODO
### syntax
- parsing-execution tests
- else if
- break
- types declaration => manage type errors parsing time
- refactor parsing error printing
- types declaration
- functions
- unit tests
- more keywords => do while
@ -53,6 +53,7 @@ var: x => val: false
- structs
- iterator, for, continue keyword
- list
- import from other files
### tools
- live interpreter
- LLVM IR translation

View File

@ -4,9 +4,11 @@ if (x) {
y = 5;
while (x) {
if (!y && z % 2) {
x = false;
z = 8;
break;
}
y = y - 1;
z = 5;
}
} else {
y = -8;

View File

@ -1,14 +1,15 @@
use std::{io::Read, env, fs::File};
use ducklang::parser::ast::build_ast;
use ducklang::interpreter::machine::Machine;
use ducklang::parser::ast::build_ast;
use std::{env, fs::File, io::Read};
pub fn main() {
for arg in env::args().skip(1) {
let mut f = File::open(&arg).expect(&format!("file {} not found", arg));
let mut f = File::open(&arg).unwrap_or_else(|_| panic!("file {} not found", arg));
let mut content = String::new();
f.read_to_string(&mut content).expect(&format!("Error in reading file {}", arg));
f.read_to_string(&mut content)
.unwrap_or_else(|_| panic!("Error in reading file {}", arg));
let ast = build_ast(&content);
let mut machine = Machine::new_with_empty_env(ast);
machine.run();
}
}
}

View File

@ -1,16 +1,24 @@
use std::fmt::{Display, Result, Formatter};
use std::fmt::{Display, Formatter, Result};
use crate::parser::syntax::Node;
use std::collections::HashMap;
pub struct Environment {
pub vars: HashMap<String, Box<Node>>
pub vars: HashMap<String, Box<Node>>,
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
impl Environment {
pub fn new() -> Environment {
Environment{ vars: HashMap::new() }
Environment {
vars: HashMap::new(),
}
}
pub fn add(&mut self, name: &str, node: Box<Node>) {
@ -27,11 +35,11 @@ impl Environment {
impl Display for Environment {
fn fmt(&self, f: &mut Formatter) -> Result {
let vars: Vec<String> = self.vars.iter()
.map(|(key, val)| {
format!("var: {0} => val: {1}", key, val)
})
let vars: Vec<String> = self
.vars
.iter()
.map(|(key, val)| format!("var: {0} => val: {1}", key, val))
.collect();
write!(f, "{}", vars.join("\n"))
}
}
}

View File

@ -1,5 +1,5 @@
use crate::parser::syntax::Node;
use super::environment::Environment;
use crate::parser::syntax::Node;
pub trait Evaluate {
fn evaluate(&self, environment: &mut Environment) -> Box<Node>;
@ -8,15 +8,9 @@ pub trait Evaluate {
impl Evaluate for Node {
fn evaluate(&self, environment: &mut Environment) -> Box<Node> {
match *self {
Node::Number(v) => {
Node::number(v)
}
Node::Boolean(v) => {
Node::boolean(v)
}
Node::DoNothing => {
Node::donothing()
}
Node::Number(v) => Node::number(v),
Node::Boolean(v) => Node::boolean(v),
Node::DoNothing => Node::donothing(),
Node::Add(ref l, ref r) => {
Node::number(l.evaluate(environment).value() + r.evaluate(environment).value())
}
@ -32,9 +26,7 @@ impl Evaluate for Node {
Node::Modulo(ref l, ref r) => {
Node::number(l.evaluate(environment).value() % r.evaluate(environment).value())
}
Node::Minus(ref t) => {
Node::number(-t.evaluate(environment).value())
}
Node::Minus(ref t) => Node::number(-t.evaluate(environment).value()),
Node::LessThan(ref l, ref r) => {
Node::boolean(l.evaluate(environment).value() < r.evaluate(environment).value())
}
@ -59,15 +51,11 @@ impl Evaluate for Node {
Node::And(ref l, ref r) => {
Node::boolean(l.evaluate(environment).truth() && r.evaluate(environment).truth())
}
Node::Not(ref t) => {
Node::boolean(!t.evaluate(environment).truth())
}
Node::Variable(ref name) => {
environment.get(&name)
}
Node::Not(ref t) => Node::boolean(!t.evaluate(environment).truth()),
Node::Variable(ref name) => environment.get(name),
Node::Assign(ref name, ref expr) => {
let reduce = expr.evaluate(environment);
environment.add(name, reduce.clone());
environment.add(name, reduce);
Node::donothing()
}
Node::If(ref condition, ref consequence, ref alternative) => {
@ -78,18 +66,23 @@ impl Evaluate for Node {
}
}
Node::Sequence(ref head, ref more) => {
head.evaluate(environment);
more.evaluate(environment);
Node::donothing()
}
Node::While(ref cond, ref body) => {
if cond.evaluate(environment).truth() {
body.evaluate(environment);
self.evaluate(environment)
} else {
Node::donothing()
if let Node::Break = *head.evaluate(environment) {
return Node::break_keyword();
}
match *more.evaluate(environment) {
Node::Break => Node::break_keyword(),
_ => Node::donothing(),
}
}
Node::While(ref cond, ref body) => {
while cond.evaluate(environment).truth() {
if let Node::Break = *body.evaluate(environment) {
break;
}
}
Node::donothing()
}
Node::Break => Node::break_keyword(),
}
}
}

View File

@ -4,12 +4,12 @@ use crate::parser::syntax::Node;
pub struct Machine {
pub expression: Box<Node>,
pub environment: Environment
pub environment: Environment,
}
impl Machine {
pub fn new(expression: Box<Node>, environment: Environment) -> Machine {
Machine{
Machine {
expression,
environment,
}
@ -18,12 +18,15 @@ impl Machine {
pub fn new_with_empty_env(expression: Box<Node>) -> Machine {
Machine {
expression,
environment: Environment::new(),
environment: Environment::default(),
}
}
pub fn run(&mut self) {
self.expression.evaluate(&mut self.environment);
println!("Final state of the program environment:\n{}", self.environment);
println!(
"Final state of the program environment:\n{}",
self.environment
);
}
}
}

View File

@ -1,3 +1,233 @@
pub mod machine;
pub mod environment;
pub mod evaluate;
pub mod environment;
pub mod machine;
#[cfg(test)]
mod tests {
use crate::parser::{ast::build_ast, syntax::Node};
use std::{fs::File, io::Read};
use super::{environment::Environment, machine::Machine};
// modify to inclue in main
fn run_file(path: &str) -> Environment {
let mut f = File::open(path).expect(&format!("file {} not found", path));
let mut content = String::new();
f.read_to_string(&mut content)
.expect(&format!("Error in reading file {}", path));
let ast = build_ast(&content);
let mut machine = Machine::new_with_empty_env(ast);
machine.run();
machine.environment
}
#[test]
fn test_assign() {
let mut environment = run_file("test_programs/assign.duck");
let x = match *environment.get("x") {
Node::Boolean(value) => value,
_ => panic!("x should be Node::Boolean"),
};
assert!(x == true);
let y = match *environment.get("y") {
Node::Number(value) => value,
_ => panic!("y should be Node::Number"),
};
assert!(y == 1);
}
#[test]
fn test_arithmetic_expr() {
let mut environment = run_file("test_programs/arithmetic_expr.duck");
let x = match *environment.get("x") {
Node::Number(value) => value,
_ => panic!("x should be Node::Number"),
};
assert!(x == 2);
let y = match *environment.get("y") {
Node::Number(value) => value,
_ => panic!("y should be Node::Number"),
};
assert!(y == 3 + 4 * (x + 1));
let z = match *environment.get("z") {
Node::Number(value) => value,
_ => panic!("z should be Node::Number"),
};
assert!(z == x % (-3 / 2));
}
#[test]
fn test_boolean_expr() {
let mut environment = run_file("test_programs/boolean_expr.duck");
let a = match *environment.get("a") {
Node::Boolean(value) => value,
_ => panic!("a should be Node::Boolean"),
};
assert!(a == (false || false));
let b = match *environment.get("b") {
Node::Boolean(value) => value,
_ => panic!("b should be Node::Boolean"),
};
assert!(b == (true || false));
let c = match *environment.get("c") {
Node::Boolean(value) => value,
_ => panic!("c should be Node::Boolean"),
};
assert!(c == (false || true));
let d = match *environment.get("d") {
Node::Boolean(value) => value,
_ => panic!("d should be Node::Boolean"),
};
assert!(d == (true || true));
let e = match *environment.get("e") {
Node::Boolean(value) => value,
_ => panic!("e should be Node::Boolean"),
};
assert!(e == (false && false));
let f = match *environment.get("f") {
Node::Boolean(value) => value,
_ => panic!("f should be Node::Boolean"),
};
assert!(f == (true && false));
let g = match *environment.get("g") {
Node::Boolean(value) => value,
_ => panic!("g should be Node::Boolean"),
};
assert!(g == (false && true));
let h = match *environment.get("h") {
Node::Boolean(value) => value,
_ => panic!("h should be Node::Boolean"),
};
assert!(h == (true && true));
let i = match *environment.get("i") {
Node::Boolean(value) => value,
_ => panic!("i should be Node::Boolean"),
};
assert!(i == !true);
let j = match *environment.get("j") {
Node::Boolean(value) => value,
_ => panic!("j should be Node::Boolean"),
};
assert!(j == !false);
}
#[test]
fn test_compare_expr() {
let mut environment = run_file("test_programs/compare_expr.duck");
let a = match *environment.get("a") {
Node::Boolean(value) => value,
_ => panic!("a should be Node::Boolean"),
};
assert!(a == (1 < 2));
let b = match *environment.get("b") {
Node::Boolean(value) => value,
_ => panic!("b should be Node::Boolean"),
};
assert!(b == (2 < 1));
let c = match *environment.get("c") {
Node::Boolean(value) => value,
_ => panic!("c should be Node::Boolean"),
};
assert!(c == (2 < 2));
let d = match *environment.get("d") {
Node::Boolean(value) => value,
_ => panic!("d should be Node::Boolean"),
};
assert!(d == (1 > 2));
let e = match *environment.get("e") {
Node::Boolean(value) => value,
_ => panic!("e should be Node::Boolean"),
};
assert!(e == (2 > 1));
let f = match *environment.get("f") {
Node::Boolean(value) => value,
_ => panic!("f should be Node::Boolean"),
};
assert!(f == (2 > 2));
let g = match *environment.get("g") {
Node::Boolean(value) => value,
_ => panic!("g should be Node::Boolean"),
};
assert!(g == (1 <= 2));
let h = match *environment.get("h") {
Node::Boolean(value) => value,
_ => panic!("h should be Node::Boolean"),
};
assert!(h == (2 <= 1));
let i = match *environment.get("i") {
Node::Boolean(value) => value,
_ => panic!("i should be Node::Boolean"),
};
assert!(i == (2 <= 2));
let j = match *environment.get("j") {
Node::Boolean(value) => value,
_ => panic!("j should be Node::Boolean"),
};
assert!(j == (1 >= 2));
let k = match *environment.get("k") {
Node::Boolean(value) => value,
_ => panic!("k should be Node::Boolean"),
};
assert!(k == (2 >= 1));
let l = match *environment.get("l") {
Node::Boolean(value) => value,
_ => panic!("l should be Node::Boolean"),
};
assert!(l == (2 >= 2));
let m = match *environment.get("m") {
Node::Boolean(value) => value,
_ => panic!("m should be Node::Boolean"),
};
assert!(m == (1 == 2));
let n = match *environment.get("n") {
Node::Boolean(value) => value,
_ => panic!("n should be Node::Boolean"),
};
assert!(n == (2 == 2));
let o = match *environment.get("o") {
Node::Boolean(value) => value,
_ => panic!("o should be Node::Boolean"),
};
assert!(o == (1 != 2));
let p = match *environment.get("p") {
Node::Boolean(value) => value,
_ => panic!("p should be Node::Boolean"),
};
assert!(p == (2 != 2));
}
#[test]
fn test_conditional() {
let mut environment = run_file("test_programs/conditional.duck");
let x = match *environment.get("x") {
Node::Boolean(value) => value,
_ => panic!("x should be Node::Boolean"),
};
assert!(x == true);
let y = match *environment.get("y") {
Node::Boolean(value) => value,
_ => panic!("y should be Node::Boolean"),
};
assert!(y == false);
let z = match *environment.get("z") {
Node::Boolean(value) => value,
_ => panic!("z should be Node::Boolean"),
};
assert!(z == true);
}
#[test]
fn test_while_loop() {
let mut environment = run_file("test_programs/while_loop.duck");
let x = match *environment.get("x") {
Node::Number(value) => value,
_ => panic!("x should be Node::Number"),
};
assert!(x == 5);
let y = match *environment.get("y") {
Node::Number(value) => value,
_ => panic!("y should be Node::Number"),
};
assert!(y == 5);
}
}

View File

@ -3,4 +3,4 @@ extern crate pest;
extern crate pest_derive;
pub mod interpreter;
pub mod parser;
pub mod parser;

View File

@ -1,6 +1,6 @@
use pest::iterators::Pair;
use pest::Parser;
use pest::pratt_parser::PrattParser;
use pest::Parser;
use super::syntax::Node;
@ -35,40 +35,44 @@ fn build_expr(pair: Pair<Rule>) -> Box<Node> {
// TODO Rule::boolean
rule => unreachable!("SimpleParser::climb expected atom, found {:?}", rule),
})
.map_infix(|lhs, op, rhs| {
match op.as_rule() {
Rule::op_add => Node::add(lhs, rhs),
Rule::op_sub => Node::sub(lhs, rhs),
Rule::op_mul => Node::mult(lhs, rhs),
Rule::op_div => Node::div(lhs, rhs),
Rule::op_mod => Node::modulo(lhs, rhs),
Rule::op_lt => Node::lessthan(lhs, rhs),
Rule::op_gt => Node::greatthan(lhs, rhs),
Rule::op_eq => Node::equal(lhs, rhs),
Rule::op_ge => Node::greatequal(lhs, rhs),
Rule::op_le => Node::lessequal(lhs, rhs),
Rule::op_ne => Node::notequal(lhs, rhs),
Rule::op_or => Node::or(lhs, rhs),
Rule::op_and => Node::and(lhs, rhs),
rule => unreachable!("SimpleParser::climb expected infix operation, found {:?}", rule),
}
.map_infix(|lhs, op, rhs| match op.as_rule() {
Rule::op_add => Node::add(lhs, rhs),
Rule::op_sub => Node::sub(lhs, rhs),
Rule::op_mul => Node::mult(lhs, rhs),
Rule::op_div => Node::div(lhs, rhs),
Rule::op_mod => Node::modulo(lhs, rhs),
Rule::op_lt => Node::lessthan(lhs, rhs),
Rule::op_gt => Node::greatthan(lhs, rhs),
Rule::op_eq => Node::equal(lhs, rhs),
Rule::op_ge => Node::greatequal(lhs, rhs),
Rule::op_le => Node::lessequal(lhs, rhs),
Rule::op_ne => Node::notequal(lhs, rhs),
Rule::op_or => Node::or(lhs, rhs),
Rule::op_and => Node::and(lhs, rhs),
rule => unreachable!(
"SimpleParser::climb expected infix operation, found {:?}",
rule
),
})
.map_prefix(|op, rhs| match op.as_rule() {
Rule::op_neg => Node::minus(rhs),
Rule::op_not => Node::not(rhs),
rule => unreachable!("SimpleParser::climb expected prefix operation, found {:?}", rule)
rule => unreachable!(
"SimpleParser::climb expected prefix operation, found {:?}",
rule
),
})
.parse(pair.into_inner())
}
fn build_if_cond(pair: Pair<Rule>) -> Box<Node> {
fn build_if_cond(pair: Pair<Rule>, in_loop: bool) -> Box<Node> {
// println!("build_if_cond::{:#?}", pair);
let mut inner = pair.into_inner();
let cond = build_expr(inner.next().unwrap());
let thenstmt = build_stats(inner.next().unwrap());
let thenstmt = build_stats(inner.next().unwrap(), in_loop);
match inner.next() {
Some(stmt) => Node::if_cond(cond, thenstmt, build_stats(stmt)),
None => Node::if_cond(cond, thenstmt, Node::donothing())
Some(stmt) => Node::if_cond(cond, thenstmt, build_stats(stmt, in_loop)),
None => Node::if_cond(cond, thenstmt, Node::donothing()),
}
}
@ -76,7 +80,7 @@ fn build_while_loop(pair: Pair<Rule>) -> Box<Node> {
// println!("build_while_loop::{:#?}", pair);
let mut inner = pair.into_inner();
let cond = build_expr(inner.next().unwrap());
let stmt = build_stats(inner.next().unwrap());
let stmt = build_stats(inner.next().unwrap(), true);
Node::while_loop(cond, stmt)
}
@ -88,28 +92,60 @@ fn build_assign(pair: Pair<Rule>) -> Box<Node> {
Node::assign(lhs, rhs)
}
fn build_stat(pair: Pair<Rule>) -> Box<Node> {
// get parsing errors out of this file
// create a error print for DuckParser error
// todo => put filename
fn parsing_error(pair: Pair<Rule>, error: &str) -> String {
let span = pair.as_span();
let (err_line, err_col) = span.start_pos().line_col();
let col_whitespaces = " ".repeat(err_line.to_string().len());
let span_len = span.end() - span.start();
let underline_chars = "^".repeat(span_len);
let error_print = vec![
format!("\nParsing error at --> {}:{}", err_line, err_col),
format!("{} |", col_whitespaces),
format!("{} | {}", err_line, span.as_str()),
format!("{} | {}", col_whitespaces, underline_chars),
format!("{} |", col_whitespaces),
format!("{} = {}", col_whitespaces, error),
];
error_print.join("\n")
}
fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Box<Node> {
match pair.as_rule() {
Rule::stat_assign => build_assign(pair),
Rule::stat_if => build_if_cond(pair),
Rule::stat_if => build_if_cond(pair, in_loop),
Rule::stat_while => build_while_loop(pair),
Rule::stat_break => {
if in_loop {
return Node::break_keyword();
}
panic!("{}", parsing_error(pair, "break keyword while not in loop"))
}
_ => unreachable!(),
}
}
fn build_stats(pair: Pair<Rule>) -> Box<Node> {
fn build_stats(pair: Pair<Rule>, in_loop: bool) -> Box<Node> {
let inner = pair.into_inner();
let nodes : Vec<_> = inner
let nodes: Vec<_> = inner
.into_iter()
.map(|pair| build_stat(pair))
.map(|pair| build_stat(pair, in_loop))
.collect();
// println!("{}", nodes.iter().fold(String::new(), |acc, node| acc + &node.to_string() + "; "));
nodes.iter().rev().fold(Node::donothing(), |acc, node| Node::sequence(node.clone(), acc))
nodes.iter().rev().fold(Node::donothing(), |acc, node| {
Node::sequence(node.clone(), acc)
})
}
pub fn build_ast(content: &str) -> Box<Node> {
let pair = DuckParser::parse(Rule::program, content)
.unwrap_or_else(|e| panic!("{}", e))
.next().unwrap();
return build_stats(pair);
}
.unwrap_or_else(|e| panic!("{}", e))
.next()
.unwrap();
build_stats(pair, false)
}

View File

@ -47,12 +47,13 @@ value = _{ boolean | number | variable | "(" ~ expr ~ ")" }
term = { op_unary* ~ value }
expr = { term ~ (op_binary ~ term)* }
stat_break = { "break" ~ ";" }
stat_assign = { variable ~ "=" ~ expr ~ ";" }
stat_while = { "while" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" }
stat_if = { ("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ "else" ~ "{" ~ stats ~ "}" ) |
("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}") }
stat = _{ ( stat_if | stat_while | stat_assign ) }
stat = _{ ( stat_if | stat_while | stat_assign | stat_break ) }
stats = { (stat)* }

View File

@ -1,12 +1,13 @@
use std::{io::Read, env, fs::File};
use ducklang::parser::ast::build_ast;
use std::{env, fs::File, io::Read};
pub fn main() {
for arg in env::args().skip(1) {
let mut f = File::open(&arg).expect(&format!("file {} not found", arg));
let mut f = File::open(&arg).unwrap_or_else(|_| panic!("file {} not found", arg));
let mut content = String::new();
f.read_to_string(&mut content).expect(&format!("Error in reading file {}", arg));
f.read_to_string(&mut content)
.unwrap_or_else(|_| panic!("Error in reading file {}", arg));
build_ast(&content);
println!("{} is a valid ducklang program", arg);
}
}
}

View File

@ -1,2 +1,54 @@
pub mod ast;
pub mod syntax;
pub mod ast;
#[cfg(test)]
mod tests {
use crate::parser::ast::build_ast;
use std::{fs::File, io::Read};
// modify to inclue in main
fn parse_file(path: &str) {
let mut f = File::open(path).expect(&format!("file {} not found", path));
let mut content = String::new();
f.read_to_string(&mut content)
.expect(&format!("Error in reading file {}", path));
build_ast(&content);
}
// can loop with some tests libs ?
#[test]
fn test_assign() {
parse_file("test_programs/assign.duck");
}
#[test]
fn test_arithmetic_expr() {
parse_file("test_programs/arithmetic_expr.duck");
}
#[test]
fn test_boolean_expr() {
parse_file("test_programs/boolean_expr.duck");
}
#[test]
fn test_compare_expr() {
parse_file("test_programs/compare_expr.duck");
}
#[test]
fn test_conditional() {
parse_file("test_programs/conditional.duck");
}
#[test]
fn test_while_loop() {
parse_file("test_programs/while_loop.duck");
}
#[test]
#[should_panic]
fn test_break_not_loop() {
parse_file("test_programs/break_not_loop.duck");
}
}

View File

@ -1,3 +1,4 @@
#![allow(clippy::should_implement_trait)]
use std::fmt::{Display, Formatter, Result};
#[derive(Clone, Debug)]
@ -25,6 +26,7 @@ pub enum Node {
If(Box<Node>, Box<Node>, Box<Node>),
Sequence(Box<Node>, Box<Node>),
While(Box<Node>, Box<Node>),
Break,
}
impl Node {
@ -88,7 +90,11 @@ impl Node {
pub fn assign(name: &str, expr: Box<Node>) -> Box<Node> {
Box::new(Node::Assign(name.to_string(), expr))
}
pub fn if_cond(condition: Box<Node>, consequence: Box<Node>, alternative: Box<Node>) -> Box<Node> {
pub fn if_cond(
condition: Box<Node>,
consequence: Box<Node>,
alternative: Box<Node>,
) -> Box<Node> {
Box::new(Node::If(condition, consequence, alternative))
}
pub fn sequence(head: Box<Node>, more: Box<Node>) -> Box<Node> {
@ -97,19 +103,22 @@ impl Node {
pub fn while_loop(cond: Box<Node>, body: Box<Node>) -> Box<Node> {
Box::new(Node::While(cond, body))
}
pub fn break_keyword() -> Box<Node> {
Box::new(Node::Break)
}
pub fn value(&self) -> i64 {
match *self {
Node::Number(value) => { value },
_ => panic!("Not a number: {:#?}", *self)
Node::Number(value) => value,
_ => panic!("Not a number: {:#?}", *self),
}
}
pub fn truth(&self) -> bool {
match *self {
Node::Boolean(b) => { b },
Node::Number(value) => { value != 0 },
_ => panic!("Cannot eval truth: {:#?}", *self)
Node::Boolean(b) => b,
Node::Number(value) => value != 0,
_ => panic!("Cannot eval truth: {:#?}", *self),
}
}
}
@ -137,9 +146,14 @@ impl Display for Node {
Node::Variable(ref value) => write!(f, "{}", value),
Node::DoNothing => write!(f, "do-nothing"),
Node::Assign(ref name, ref expr) => write!(f, "{0} = {1}", name, expr),
Node::If(ref condition, ref consequence, ref alternative) => write!(f, "if ({0}) {1} else {2}", condition, consequence, alternative),
Node::If(ref condition, ref consequence, ref alternative) => write!(
f,
"if ({0}) {1} else {2}",
condition, consequence, alternative
),
Node::Sequence(ref head, ref more) => write!(f, "{0}; {1}", head, more),
Node::While(ref cond, ref body) => write!(f, "while ({0}) {1}", cond, body)
Node::While(ref cond, ref body) => write!(f, "while ({0}) {1}", cond, body),
Node::Break => write!(f, "break"),
}
}
}
}

View File

@ -0,0 +1,3 @@
x = 2;
y = 3 + 4 * (x + 1);
z = x % (-3 / 2);

View File

@ -0,0 +1,2 @@
x = true;
y = 1;

View File

@ -0,0 +1,10 @@
a = false || false;
b = true || false;
c = false || true;
d = true || true;
e = false && false;
f = true && false;
g = false && true;
h = true && true;
i = !true;
j = !false;

View File

@ -0,0 +1,5 @@
x = 2;
while (x) {
x = x - 1;
}
break;

View File

@ -0,0 +1,16 @@
a = 1 < 2;
b = 2 < 1;
c = 2 < 2;
d = 1 > 2;
e = 2 > 1;
f = 2 > 2;
g = 1 <= 2;
h = 2 <= 1;
i = 2 <= 2;
j = 1 >= 2;
k = 2 >= 1;
l = 2 >= 2;
m = 1 == 2;
n = 2 == 2;
o = 1 != 2;
p = 2 != 2;

View File

@ -0,0 +1,14 @@
if (true) {
x = true;
} else {
x = false;
}
if (false) {
y = true;
} else {
y = false;
}
z = false;
if (true) {
z = true;
}

View File

@ -0,0 +1,13 @@
x = 0;
y = 0;
while (x < 5) {
x = x + 1;
}
while (true) {
y = y + 1;
if (y == 5) {
break;
}
}