feat(string): string, chars and indexing

main
flavien 2023-12-11 22:18:33 +01:00
parent 20a1c526e0
commit 312aef4475
No known key found for this signature in database
10 changed files with 355 additions and 138 deletions

View File

@ -57,14 +57,16 @@ var: z => val: 8
## TODO
### syntax
- functions OK
- strings
- print statement
- types declaration
- types inference
- array / list / string / map => iterators
- print statement OK
- string / char => indexing
- array / list / map => iterators
- read / write
- types declaration
- type checking before runtime
- types inference
- program arguments
- structs
- range
- import from other files
- refactor parsing error printing
- unit tests

View File

@ -7,6 +7,7 @@ z = 3 + 4 * (2 + 1); // good order of op
if (x) {
y = 5;
while (x) {
print y;
if (!y && z % 2) {
break;
} else if (y == 3) { // never reached because of continue
@ -21,4 +22,10 @@ if (x) {
// never reached
y = -8;
x = true;
}
}
fn hello(name) {
return "Hello, " + name + '!';
}
print hello("flavien");

View File

@ -1,6 +1,45 @@
use std::rc::Rc;
use super::{environment::Environment, evaluate::Evaluate};
use crate::parser::syntax::{BinaryOp, Expr, UnaryOp, Value};
pub trait ValueCompute {
fn int(&self) -> Result<i64, String>;
fn truth(&self) -> Result<bool, String>;
fn str(&self) -> Result<Rc<String>, String>;
fn char(&self) -> Result<char, String>;
}
impl ValueCompute for Value {
fn int(&self) -> Result<i64, String> {
match self {
Value::Number(n) => Ok(*n),
_ => Err(format!("{} is not a number", self)),
}
}
fn truth(&self) -> Result<bool, String> {
match self {
Value::Boolean(b) => Ok(*b),
Value::Number(value) => Ok(*value != 0),
Value::String(string) => Ok(!string.is_empty()),
Value::Char(c) => Ok(*c != '\0'),
_ => Err(format!("{} is not a logical value", self)),
}
}
fn str(&self) -> Result<Rc<String>, String> {
match self {
Value::String(s) => Ok(s.clone()),
_ => Err(format!("{} is not a string", self)),
}
}
fn char(&self) -> Result<char, String> {
match self {
Value::Char(c) => Ok(*c),
_ => Err(format!("{} is not a char", self)),
}
}
}
pub trait UnaryCompute {
fn compute(&self, env: &mut dyn Environment, expr: &Expr) -> Result<Value, String>;
}
@ -8,8 +47,20 @@ pub trait UnaryCompute {
impl UnaryCompute for UnaryOp {
fn compute(&self, env: &mut dyn Environment, expr: &Expr) -> Result<Value, String> {
Ok(match self {
UnaryOp::Minus => Value::Number(-expr.evaluate(env)?.value()?),
UnaryOp::Minus => Value::Number(-expr.evaluate(env)?.int()?),
UnaryOp::Not => Value::Boolean(!expr.evaluate(env)?.truth()?),
UnaryOp::Index(index) => {
let index = index.evaluate(env)?.int()?;
let value = expr.evaluate(env)?.str()?;
if index < 0 || index >= value.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
index,
value.len()
));
}
Value::Char(value.chars().nth(index as usize).unwrap())
}
})
}
}
@ -32,43 +83,88 @@ impl BinaryCompute for BinaryOp {
right: &Expr,
) -> Result<Value, String> {
match self {
BinaryOp::Addition => Ok(Value::Number(
left.evaluate(env)?.value()? + right.evaluate(env)?.value()?,
)),
BinaryOp::Subtraction => Ok(Value::Number(
left.evaluate(env)?.value()? - right.evaluate(env)?.value()?,
)),
BinaryOp::Multiplication => Ok(Value::Number(
left.evaluate(env)?.value()? * right.evaluate(env)?.value()?,
)),
BinaryOp::Division => Ok(Value::Number(
left.evaluate(env)?.value()? / right.evaluate(env)?.value()?,
)),
BinaryOp::Modulo => Ok(Value::Number(
left.evaluate(env)?.value()? % right.evaluate(env)?.value()?,
)),
BinaryOp::Addition => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l + r)),
(Value::String(l), Value::Char(r)) => {
Ok(Value::String(Rc::new(format!("{}{}", l, r))))
}
(Value::Char(l), Value::String(r)) => {
Ok(Value::String(Rc::new(format!("{}{}", l, r))))
}
(Value::Char(l), Value::Char(r)) => Ok(Value::Char((l as u8 + r as u8) as char)),
(Value::Char(l), Value::Number(r)) => Ok(Value::Char((l as u8 + r as u8) as char)),
(Value::Number(l), Value::Char(r)) => Ok(Value::Char((l as u8 + r as u8) as char)),
(Value::String(l), Value::String(r)) => {
Ok(Value::String(Rc::new(format!("{}{}", l, r))))
}
(l, r) => Err(format!("Cannot add {} to {}", l, r)),
},
BinaryOp::Subtraction => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l - r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Char((l as u8 - r as u8) as char)),
(Value::Char(l), Value::Number(r)) => Ok(Value::Char((l as u8 - r as u8) as char)),
(Value::Number(l), Value::Char(r)) => Ok(Value::Char((l as u8 - r as u8) as char)),
(l, r) => Err(format!("Cannot subtract {} from {}", r, l)),
},
BinaryOp::Multiplication => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l * r)),
(Value::Char(l), Value::Number(r)) => Ok(Value::String(Rc::new(
(0..r).map(|_| l).collect::<String>(),
))),
(Value::Number(l), Value::Char(r)) => Ok(Value::String(Rc::new(
(0..l).map(|_| r).collect::<String>(),
))),
(l, r) => Err(format!("Cannot multiply {} by {}", l, r)),
},
BinaryOp::Division => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l / r)),
(l, r) => Err(format!("Cannot divide {} by {}", l, r)),
},
BinaryOp::Modulo => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Number(l % r)),
(l, r) => Err(format!("Cannot modulo {} by {}", l, r)),
},
BinaryOp::Equal => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l == r)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l == r)),
_ => Err("Cannot compare different types".to_string()),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l == r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l == r)),
(l, r) => Err(format!("Cannot evaluate equality between {} and {}", l, r)),
},
BinaryOp::NotEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l != r)),
(Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l != r)),
_ => Err("Cannot compare different types".to_string()),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l != r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l != r)),
(l, r) => Err(format!(
"Cannot evaluate inequality between {} and {}",
l, r
)),
},
BinaryOp::GreaterEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l >= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l >= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l >= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::GreaterThan => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l > r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l > r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l > r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::LesserEqual => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l <= r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l <= r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l <= r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::LesserThan => match (left.evaluate(env)?, right.evaluate(env)?) {
(Value::Number(l), Value::Number(r)) => Ok(Value::Boolean(l < r)),
(Value::String(l), Value::String(r)) => Ok(Value::Boolean(l < r)),
(Value::Char(l), Value::Char(r)) => Ok(Value::Boolean(l < r)),
(l, r) => Err(format!("Cannot compare {} and {}", l, r)),
},
BinaryOp::GreaterEqual => Ok(Value::Boolean(
left.evaluate(env)?.value() >= right.evaluate(env)?.value(),
)),
BinaryOp::GreaterThan => Ok(Value::Boolean(
left.evaluate(env)?.value() > right.evaluate(env)?.value(),
)),
BinaryOp::LesserEqual => Ok(Value::Boolean(
left.evaluate(env)?.value() <= right.evaluate(env)?.value(),
)),
BinaryOp::LesserThan => Ok(Value::Boolean(
left.evaluate(env)?.value() < right.evaluate(env)?.value(),
)),
BinaryOp::And => Ok(Value::Boolean(
left.evaluate(env)?.truth()? && right.evaluate(env)?.truth()?,
)),

View File

@ -1,4 +1,7 @@
use std::rc::Rc;
use super::{
compute::ValueCompute,
environment::{Environment, GlobalScope},
evaluate::Evaluate,
};
@ -31,7 +34,6 @@ impl Execution for Vec<Stat> {
impl Execution for Stat {
fn execute(&self, environment: &mut dyn Environment) -> Result<ControlFlow, String> {
// println!("statement: {}", &self);
match *self {
Stat::Condition(ref condition, ref consequence, ref alternative) => {
if condition.evaluate(environment)?.truth()? {
@ -49,20 +51,42 @@ impl Execution for Stat {
}
Ok(ControlFlow::Next)
}
Stat::Assignment(ref name, ref expr) => {
let value = expr.evaluate(environment)?;
environment.add(name, value);
// println!("environment:\n{}", environment);
Stat::Assignment(ref name, ref index, ref expr) => {
if let Some(index) = index {
let index = index.evaluate(environment)?.int()?;
let value = expr.evaluate(environment)?.char()?;
let mut chars: Vec<char> = environment
.get(name)
.ok_or_else(|| format!("Variable {} not found", name))?
.str()?
.chars()
.collect();
if index < 0 || index >= chars.len() as i64 {
return Err(format!(
"Index {} out of bounds for string of length {}",
index,
chars.len()
));
}
chars[index as usize] = value;
environment.add(name, Value::String(Rc::new(chars.into_iter().collect())));
} else {
let value = expr.evaluate(environment)?;
environment.add(name, value);
}
Ok(ControlFlow::Next)
}
Stat::Break => Ok(ControlFlow::Break),
Stat::Continue => Ok(ControlFlow::Continue),
Stat::Define(ref name, ref args, ref body) => {
environment.add(name, Value::Function(args.clone(), body.clone()));
// println!("environment:\n{}", environment);
Ok(ControlFlow::Next)
}
Stat::Return(ref expr) => Ok(ControlFlow::Return(expr.evaluate(environment)?)),
Stat::Print(ref expr) => {
println!("{}", expr.evaluate(environment)?);
Ok(ControlFlow::Next)
}
}
}
}
@ -91,7 +115,7 @@ impl Machine {
pub fn run(&mut self, statements: Vec<Stat>) -> Result<i64, String> {
for statement in statements {
if let ControlFlow::Return(val) = statement.execute(&mut self.environment)? {
return val.value();
return val.int();
}
}
Ok(0)

View File

@ -269,4 +269,34 @@ mod tests {
};
assert!(z == 0);
}
#[test]
fn test_string_char() {
let environment = run_file("test_programs/string_char.duck");
let x = match environment.get("x") {
Some(Value::String(value)) => value,
_ => panic!("x should be Value::String"),
};
assert!(*x == "hallo ");
let y = match environment.get("y") {
Some(Value::String(value)) => value,
_ => panic!("y should be Value::String"),
};
assert!(*y == "world");
let z = match environment.get("z") {
Some(Value::String(value)) => value,
_ => panic!("z should be Value::String"),
};
assert!(*z == "hello world!");
let a = match environment.get("a") {
Some(Value::String(value)) => value,
_ => panic!("a should be Value::String"),
};
assert!(*a == "!!!");
let b = match environment.get("b") {
Some(Value::Char(value)) => value,
_ => panic!("b should be Value::Char"),
};
assert!(b == 'l');
}
}

View File

@ -1,3 +1,5 @@
use std::rc::Rc;
use pest::iterators::Pair;
use pest::pratt_parser::PrattParser;
use pest::Parser;
@ -21,6 +23,7 @@ lazy_static::lazy_static! {
.op(Op::infix(op_add, Left) | Op::infix(op_sub, Left))
.op(Op::infix(op_mul, Left) | Op::infix(op_div, Left) | Op::infix(op_mod, Left))
.op(Op::prefix(op_neg) | Op::prefix(op_not))
.op(Op::postfix(op_index))
};
}
@ -55,17 +58,25 @@ fn build_call(pair: Pair<Rule>) -> Result<Expr, String> {
.into_iter()
.map(|arg| build_expr(arg).unwrap())
.collect();
Ok(Expr::call(name, arg_exprs))
Ok(Expr::Call(name, arg_exprs))
}
fn build_expr(pair: Pair<Rule>) -> Result<Expr, String> {
PRATT_PARSER
.map_primary(|primary| match primary.as_rule() {
Rule::term | Rule::expr => build_expr(primary),
Rule::number => Ok(Expr::litteral(Value::number(primary.as_str())?)),
Rule::boolean => Ok(Expr::litteral(Value::boolean(primary.as_str())?)),
Rule::variable => Ok(Expr::variable(primary.as_str())),
Rule::number => Ok(Expr::Litteral(Value::number(primary.as_str())?)),
Rule::boolean => Ok(Expr::Litteral(Value::boolean(primary.as_str())?)),
Rule::variable => Ok(Expr::Variable(primary.as_str().to_string())),
Rule::call => build_call(primary),
Rule::string => {
let litteral = primary.as_str();
let value = litteral[1..litteral.len() - 1].to_string();
Ok(Expr::Litteral(Value::String(Rc::new(value))))
}
Rule::char => Ok(Expr::Litteral(Value::Char(
primary.as_str().chars().nth(1).unwrap(),
))),
rule => Err(format!(
"atom expected number, boolean or variable, found {:?}",
rule
@ -74,62 +85,62 @@ fn build_expr(pair: Pair<Rule>) -> Result<Expr, String> {
.map_infix(|lhs, op, rhs| match (lhs, op.as_rule(), rhs) {
(Err(err), _, _) | (_, _, Err(err)) => Err(err),
(Ok(lhs), Rule::op_or, Ok(rhs)) => {
Ok(Expr::binary_op(Box::new(lhs), Box::new(rhs), BinaryOp::Or))
Ok(Expr::BinaryOp(Box::new(lhs), Box::new(rhs), BinaryOp::Or))
}
(Ok(lhs), Rule::op_and, Ok(rhs)) => {
Ok(Expr::binary_op(Box::new(lhs), Box::new(rhs), BinaryOp::And))
Ok(Expr::BinaryOp(Box::new(lhs), Box::new(rhs), BinaryOp::And))
}
(Ok(lhs), Rule::op_lt, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_lt, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::LesserThan,
)),
(Ok(lhs), Rule::op_gt, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_gt, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::GreaterThan,
)),
(Ok(lhs), Rule::op_eq, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_eq, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Equal,
)),
(Ok(lhs), Rule::op_ge, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_ge, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::GreaterEqual,
)),
(Ok(lhs), Rule::op_le, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_le, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::LesserEqual,
)),
(Ok(lhs), Rule::op_ne, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_ne, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::NotEqual,
)),
(Ok(lhs), Rule::op_add, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_add, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Addition,
)),
(Ok(lhs), Rule::op_sub, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_sub, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Subtraction,
)),
(Ok(lhs), Rule::op_mul, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_mul, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Multiplication,
)),
(Ok(lhs), Rule::op_div, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_div, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Division,
)),
(Ok(lhs), Rule::op_mod, Ok(rhs)) => Ok(Expr::binary_op(
(Ok(lhs), Rule::op_mod, Ok(rhs)) => Ok(Expr::BinaryOp(
Box::new(lhs),
Box::new(rhs),
BinaryOp::Modulo,
@ -141,28 +152,48 @@ fn build_expr(pair: Pair<Rule>) -> Result<Expr, String> {
})
.map_prefix(|op, rhs| match (op.as_rule(), rhs) {
(_, Err(err)) => Err(err),
(Rule::op_neg, Ok(rhs)) => Ok(Expr::unary_op(Box::new(rhs), UnaryOp::Minus)),
(Rule::op_not, Ok(rhs)) => Ok(Expr::unary_op(Box::new(rhs), UnaryOp::Not)),
(Rule::op_neg, Ok(rhs)) => Ok(Expr::UnaryOp(Box::new(rhs), UnaryOp::Minus)),
(Rule::op_not, Ok(rhs)) => Ok(Expr::UnaryOp(Box::new(rhs), UnaryOp::Not)),
(rule, _) => Err(format!(
"unary operation expected operator, found {:?}",
rule
)),
})
.map_postfix(|lhs, op| match (lhs, op.as_rule()) {
(Err(err), _) => Err(err),
(Ok(lhs), Rule::op_index) => {
let index_expr = build_expr(op.into_inner().next().unwrap())?;
Ok(Expr::UnaryOp(
Box::new(lhs),
UnaryOp::Index(Box::new(index_expr)),
))
}
(Ok(_), rule) => Err(format!(
"indexing operation expected operator, found {:?}",
rule
)),
})
.parse(pair.into_inner())
}
fn build_assign(pair: Pair<Rule>) -> Result<Stat, String> {
let mut inner = pair.into_inner();
let token_len = inner.len();
let lhs = inner.next().unwrap().as_str();
if token_len == 2 {
let rhs = build_expr(inner.next().unwrap())?;
return Ok(Stat::Assignment(lhs.to_string(), None, rhs));
}
let index = build_expr(inner.next().unwrap())?;
let rhs = build_expr(inner.next().unwrap())?;
Ok(Stat::assign(lhs, rhs))
Ok(Stat::Assignment(lhs.to_string(), Some(index), rhs))
}
fn build_while_loop(pair: Pair<Rule>) -> Result<Stat, String> {
let mut inner = pair.into_inner();
let cond = build_expr(inner.next().unwrap())?;
let stmt = build_stats(inner.next().unwrap(), true)?;
Ok(Stat::while_loop(cond, stmt))
Ok(Stat::Loop(cond, stmt))
}
fn build_if_cond(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
@ -171,19 +202,19 @@ fn build_if_cond(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
let thenstmt = build_stats(inner.next().unwrap(), in_loop)?;
match inner.next() {
Some(stmt) => match stmt.as_rule() {
Rule::stats => Ok(Stat::if_cond(
Rule::stats => Ok(Stat::Condition(
cond,
thenstmt,
Some(build_stats(stmt, in_loop)?),
)),
Rule::stat_elif => Ok(Stat::if_cond(
Rule::stat_elif => Ok(Stat::Condition(
cond,
thenstmt,
Some(vec![build_stat(stmt, in_loop)?]),
)),
rule => Err(format!("conditional statement expected, found {:?}", rule)),
},
None => Ok(Stat::if_cond(cond, thenstmt, None)),
None => Ok(Stat::Condition(cond, thenstmt, None)),
}
}
@ -196,12 +227,17 @@ fn build_define(pair: Pair<Rule>) -> Result<Stat, String> {
.map(|arg| arg.as_str().to_string())
.collect();
let stmt = build_stats(inner.next().unwrap(), false)?;
Ok(Stat::define(name, arg_names, stmt))
Ok(Stat::Define(name, Rc::new(arg_names), Rc::new(stmt)))
}
fn build_return(pair: Pair<Rule>) -> Result<Stat, String> {
let expr = build_expr(pair.into_inner().next().unwrap())?;
Ok(Stat::return_keyword(expr))
Ok(Stat::Return(expr))
}
fn build_print(pair: Pair<Rule>) -> Result<Stat, String> {
let expr = build_expr(pair.into_inner().next().unwrap())?;
Ok(Stat::Print(expr))
}
fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
@ -211,18 +247,19 @@ fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
Rule::stat_while => build_while_loop(pair),
Rule::stat_break => {
if in_loop {
return Ok(Stat::break_keyword());
return Ok(Stat::Break);
}
Err(parsing_error(pair, "break keyword while not in loop"))
}
Rule::stat_continue => {
if in_loop {
return Ok(Stat::continue_keyword());
return Ok(Stat::Continue);
}
Err(parsing_error(pair, "continue keyword while not in loop"))
}
Rule::stat_define => build_define(pair),
Rule::stat_return => build_return(pair),
Rule::stat_print => build_print(pair),
rule => Err(format!("Statement expected, found {:?}", rule)),
}
}
@ -239,7 +276,52 @@ fn build_stats(pair: Pair<Rule>, in_loop: bool) -> Result<Vec<Stat>, String> {
pub fn build_ast(content: &str) -> Result<Vec<Stat>, String> {
let pair = match DuckParser::parse(Rule::program, content) {
Ok(mut pairs) => pairs.next().unwrap(),
Err(err) => return Err(format!("{}", err)),
Err(err) => {
return Err(format!(
"{}",
err.renamed_rules(|rule| {
match *rule {
Rule::program => "program".to_owned(),
Rule::stat => "statement".to_owned(),
Rule::stat_assign => "assignment".to_owned(),
Rule::stat_if => "if statement".to_owned(),
Rule::stat_elif => "elif statement".to_owned(),
Rule::stat_while => "while statement".to_owned(),
Rule::stat_break => "break statement".to_owned(),
Rule::stat_continue => "continue statement".to_owned(),
Rule::stat_define => "define statement".to_owned(),
Rule::stat_return => "return statement".to_owned(),
Rule::stat_print => "print statement".to_owned(),
Rule::expr => "expression".to_owned(),
Rule::term => "term".to_owned(),
Rule::call => "function call".to_owned(),
Rule::variable => "variable".to_owned(),
Rule::number => "number".to_owned(),
Rule::boolean => "boolean".to_owned(),
Rule::op_or => "||".to_owned(),
Rule::op_and => "&&".to_owned(),
Rule::op_lt => "<".to_owned(),
Rule::op_gt => ">".to_owned(),
Rule::op_eq => "==".to_owned(),
Rule::op_ge => ">=".to_owned(),
Rule::op_le => "<=".to_owned(),
Rule::op_ne => "!=".to_owned(),
Rule::op_add => "+".to_owned(),
Rule::op_sub => "-".to_owned(),
Rule::op_mul => "*".to_owned(),
Rule::op_div => "/".to_owned(),
Rule::op_mod => "%".to_owned(),
Rule::op_neg => "-".to_owned(),
Rule::op_not => "!".to_owned(),
Rule::op_index => "[]".to_owned(),
Rule::WHITESPACE => "whitespace".to_owned(),
Rule::COMMENT => "comment".to_owned(),
Rule::EOI => "end of input".to_owned(),
_ => unreachable!(),
}
})
))
}
};
build_stats(pair, false)
}

View File

@ -8,6 +8,8 @@ block_comment = @{ "/*" ~ ((!("*/") ~ ANY) | block_comment)* ~ "*/" }
args_call = { (expr ~ ("," ~ expr)*)? }
call = { variable ~ "(" ~ args_call ~ ")" }
string = @{ "\"" ~ (!("\"") ~ ANY)* ~ "\"" }
char = @{ "'" ~ (!("'") ~ ANY) ~ "'" }
variable = @ { (alpha) ~ (alpha | digit)* }
number = @ { (digit)+ }
boolean = @ { "true" | "false" }
@ -27,6 +29,7 @@ op_or = { "||" }
op_and = { "&&" }
op_not = { "!" }
op_neg = { "-" }
op_index = { "[" ~ expr ~ "]" }
op_binary = _{
op_add |
@ -48,24 +51,24 @@ op_unary = _{
op_not
}
value = _{ call | boolean | number | variable | "(" ~ expr ~ ")" }
term = { op_unary* ~ value }
value = _{ call | boolean | number | variable | string | char | "(" ~ expr ~ ")" }
term = { op_unary* ~ value ~ op_index?}
expr = { term ~ (op_binary ~ term)* }
args_define = { (variable ~ ("," ~ variable)*)? }
stat_print = { "print" ~ expr ~ ";" }
stat_return = { "return" ~ expr ~ ";" }
stat_define = { "fn" ~ variable ~ "(" ~ args_define ~ ")" ~ "{" ~ stats ~ "}" }
stat_continue = { "continue" ~ ";" }
stat_break = { "break" ~ ";" }
stat_assign = { variable ~ "=" ~ expr ~ ";" }
stat_assign = { variable ~ op_index? ~ "=" ~ expr ~ ";" }
stat_while = { "while" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" }
stat_elif = { ("else if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ "else" ~ "{" ~ stats ~ "}" ) |
("else if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ (stat_elif)?) }
stat_if = { ("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ "else" ~ "{" ~ stats ~ "}" ) |
("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ (stat_elif)?) }
stat = _{ ( stat_if | stat_while | stat_assign | stat_break | stat_continue | stat_define | stat_return ) }
stat = _{ ( stat_if | stat_while | stat_assign | stat_break | stat_continue | stat_define | stat_return | stat_print ) }
stats = { (stat)* }

View File

@ -56,4 +56,9 @@ mod tests {
fn test_functions() {
parse_file("test_programs/functions.duck");
}
#[test]
fn test_string_char() {
parse_file("test_programs/string_char.duck");
}
}

View File

@ -41,10 +41,11 @@ impl Display for BinaryOp {
}
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub enum UnaryOp {
Minus,
Not,
Index(Box<Expr>),
}
impl Display for UnaryOp {
@ -52,6 +53,7 @@ impl Display for UnaryOp {
match self {
UnaryOp::Minus => write!(f, "-"),
UnaryOp::Not => write!(f, "!"),
UnaryOp::Index(expr) => write!(f, "[{}]", expr),
}
}
}
@ -62,6 +64,8 @@ pub enum Value {
Number(i64),
Boolean(bool),
Function(Rc<Vec<String>>, Rc<Vec<Stat>>),
String(Rc<String>),
Char(char),
None,
}
@ -78,21 +82,6 @@ impl Value {
Err(_) => Err(format!("{} is not a boolean", raw)),
}
}
// TODO move to compute.rs
pub fn value(&self) -> Result<i64, String> {
match self {
Value::Number(n) => Ok(*n),
_ => Err(format!("{} is not a number", self)),
}
}
// TODO move to compute.rs
pub fn truth(&self) -> Result<bool, String> {
match self {
Value::Boolean(b) => Ok(*b),
Value::Number(value) => Ok(*value != 0),
_ => Err(format!("{} is not a logical value", self)),
}
}
}
impl Display for Value {
@ -101,6 +90,8 @@ impl Display for Value {
Value::Number(n) => write!(f, "{}", n),
Value::Boolean(b) => write!(f, "{}", b),
Value::Function(args, _) => write!(f, "fn({})", args.join(", ")),
Value::String(s) => write!(f, "{}", s),
Value::Char(c) => write!(f, "{}", c),
Value::None => write!(f, "None"),
}
}
@ -116,31 +107,16 @@ pub enum Expr {
Call(String, Vec<Expr>),
}
impl Expr {
pub fn litteral(value: Value) -> Expr {
Expr::Litteral(value)
}
pub fn variable(name: &str) -> Expr {
Expr::Variable(name.to_string())
}
pub fn binary_op(left: Box<Expr>, right: Box<Expr>, op: BinaryOp) -> Expr {
Expr::BinaryOp(left, right, op)
}
pub fn unary_op(term: Box<Expr>, op: UnaryOp) -> Expr {
Expr::UnaryOp(term, op)
}
pub fn call(name: String, args: Vec<Expr>) -> Expr {
Expr::Call(name, args)
}
}
impl Display for Expr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Expr::Litteral(value) => write!(f, "{}", value),
Expr::Variable(name) => write!(f, "{}", name),
Expr::BinaryOp(left, right, op) => write!(f, "{} {} {}", left, op, right),
Expr::UnaryOp(term, op) => write!(f, "{}{}", op, term),
Expr::UnaryOp(term, op) => match op {
UnaryOp::Index(index) => write!(f, "{}{}", term, index),
_ => write!(f, "{}{}", op, term),
},
Expr::Call(name, args) => {
write!(f, "{}(", name)?;
let expressions = args
@ -158,35 +134,12 @@ impl Display for Expr {
pub enum Stat {
Condition(Expr, Vec<Stat>, Option<Vec<Stat>>),
Loop(Expr, Vec<Stat>),
Assignment(String, Expr),
Assignment(String, Option<Expr>, Expr),
Break,
Continue,
Define(String, Rc<Vec<String>>, Rc<Vec<Stat>>),
Return(Expr),
}
impl Stat {
pub fn assign(name: &str, expr: Expr) -> Stat {
Stat::Assignment(name.to_string(), expr)
}
pub fn break_keyword() -> Stat {
Stat::Break
}
pub fn continue_keyword() -> Stat {
Stat::Continue
}
pub fn while_loop(condition: Expr, body: Vec<Stat>) -> Stat {
Stat::Loop(condition, body)
}
pub fn if_cond(condition: Expr, body: Vec<Stat>, else_body: Option<Vec<Stat>>) -> Stat {
Stat::Condition(condition, body, else_body)
}
pub fn define(name: String, args: Vec<String>, body: Vec<Stat>) -> Stat {
Stat::Define(name, Rc::new(args), Rc::new(body))
}
pub fn return_keyword(expr: Expr) -> Stat {
Stat::Return(expr)
}
Print(Expr),
}
impl Display for Stat {
@ -214,7 +167,13 @@ impl Display for Stat {
}
write!(f, "}}")
}
Stat::Assignment(name, expr) => write!(f, "{} = {};", name, expr),
Stat::Assignment(name, index, expr) => {
if let Some(index) = index {
write!(f, "{}[{}] = {};", name, index, expr)
} else {
write!(f, "{} = {};", name, expr)
}
}
Stat::Break => write!(f, "break;"),
Stat::Continue => write!(f, "continue;"),
Stat::Define(name, args, body) => {
@ -227,6 +186,7 @@ impl Display for Stat {
write!(f, "}}")
}
Stat::Return(expr) => write!(f, "return {};", expr),
Stat::Print(expr) => write!(f, "print {};", expr),
}
}
}

View File

@ -0,0 +1,8 @@
x = "hello ";
y = "world";
a = '!';
z = x + y + a;
print z ;
a = a * 3;
x[1] = 'a';
b = x[2];