Compare commits
4 Commits
35b12c19f4
...
20a1c526e0
Author | SHA1 | Date |
---|---|---|
flavien | 20a1c526e0 | |
flavien | 8092cf761f | |
flavien | 252c23e889 | |
flavien | 056d304b73 |
|
@ -1 +1,3 @@
|
|||
/target
|
||||
/.vscode
|
||||
/dist
|
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
28
Cargo.toml
|
@ -7,8 +7,14 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
pest = "2.0"
|
||||
pest_derive = "2.0"
|
||||
pest = "2.7.5"
|
||||
pest_derive = "2.7.5"
|
||||
# web
|
||||
egui = "0.24.0"
|
||||
eframe = { version = "0.24.0", default-features = false, features = [
|
||||
"default_fonts", # Embed the default egui fonts.
|
||||
"glow", # Use the glow rendering backend. Alternative: "wgpu".
|
||||
] }
|
||||
|
||||
[lib]
|
||||
name = "ducklang"
|
||||
|
@ -21,3 +27,21 @@ path = "src/parser/duck_parser.rs"
|
|||
[[bin]]
|
||||
name = "duck_interpreter"
|
||||
path = "src/interpreter/duck_interpreter.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "duck_web"
|
||||
path = "src/web/main.rs"
|
||||
|
||||
# native:
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
# web:
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
tracing-wasm = "0.2.1"
|
||||
wasm-bindgen-futures = "0.4.39"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 's'
|
||||
lto = true
|
||||
|
|
30
README.md
30
README.md
|
@ -12,11 +12,14 @@ Written in Rust with pest PEG parser.
|
|||
|
||||
<br>
|
||||
|
||||
### Branches
|
||||
I have currently 3 branches that start from the main branch:
|
||||
- `egui`: an attempt to create a code editor and interpreter in wasm for web embedding
|
||||
- `features`: new features for the language but only implemented with interpreter
|
||||
- `compiler`: an attempt to create a compiler from LLVM
|
||||
### TODO playground
|
||||
- compile time reading of the example file
|
||||
- syntax color
|
||||
- line numbers
|
||||
- better panel title
|
||||
- panel tabs
|
||||
- print / string => language
|
||||
- console for print
|
||||
|
||||
### Example
|
||||
```
|
||||
|
@ -53,21 +56,20 @@ var: z => val: 8
|
|||
|
||||
## TODO
|
||||
### syntax
|
||||
- functions OK
|
||||
- strings
|
||||
- print statement
|
||||
- types declaration
|
||||
- refactor parsing error printing
|
||||
- functions
|
||||
- unit tests
|
||||
- more keywords => do while
|
||||
- string
|
||||
- array
|
||||
- types inference
|
||||
- array / list / string / map => iterators
|
||||
- read / write
|
||||
- program arguments
|
||||
- structs
|
||||
- iterator, for, continue keyword
|
||||
- list
|
||||
- import from other files
|
||||
- refactor parsing error printing
|
||||
- unit tests
|
||||
### tools
|
||||
- live interpreter
|
||||
- repl
|
||||
- LLVM IR translation
|
||||
- JIT
|
||||
- compilation
|
|
@ -8,10 +8,12 @@ if (x) {
|
|||
y = 5;
|
||||
while (x) {
|
||||
if (!y && z % 2) {
|
||||
z = 8;
|
||||
break;
|
||||
} else if (y == 3) {
|
||||
} else if (y == 3) { // never reached because of continue
|
||||
z = 5;
|
||||
} else if (y == 4) {
|
||||
y = y - 2;
|
||||
continue;
|
||||
}
|
||||
y = y - 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link data-trunk rel="rust" data-bin="duck_web" data-wasm-opt="2" />
|
||||
|
||||
<style>
|
||||
html {
|
||||
/* Remove touch delay: */
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
canvas {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
</body>
|
||||
|
||||
</html>
|
Binary file not shown.
|
@ -2,69 +2,79 @@ use super::{environment::Environment, evaluate::Evaluate};
|
|||
use crate::parser::syntax::{BinaryOp, Expr, UnaryOp, Value};
|
||||
|
||||
pub trait UnaryCompute {
|
||||
fn compute(&self, environment: &mut Environment, expr: &Expr) -> Value;
|
||||
fn compute(&self, env: &mut dyn Environment, expr: &Expr) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
impl UnaryCompute for UnaryOp {
|
||||
fn compute(&self, environment: &mut Environment, expr: &Expr) -> Value {
|
||||
match self {
|
||||
UnaryOp::Minus => Value::Number(-expr.evaluate(environment).value()),
|
||||
UnaryOp::Not => Value::Boolean(!expr.evaluate(environment).truth()),
|
||||
}
|
||||
fn compute(&self, env: &mut dyn Environment, expr: &Expr) -> Result<Value, String> {
|
||||
Ok(match self {
|
||||
UnaryOp::Minus => Value::Number(-expr.evaluate(env)?.value()?),
|
||||
UnaryOp::Not => Value::Boolean(!expr.evaluate(env)?.truth()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BinaryCompute {
|
||||
fn compute(&self, environment: &mut Environment, left: &Expr, right: &Expr) -> Value;
|
||||
fn compute(
|
||||
&self,
|
||||
env: &mut dyn Environment,
|
||||
left: &Expr,
|
||||
right: &Expr,
|
||||
) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
// add visitor to match types
|
||||
impl BinaryCompute for BinaryOp {
|
||||
fn compute(&self, environment: &mut Environment, left: &Expr, right: &Expr) -> Value {
|
||||
fn compute(
|
||||
&self,
|
||||
env: &mut dyn Environment,
|
||||
left: &Expr,
|
||||
right: &Expr,
|
||||
) -> Result<Value, String> {
|
||||
match self {
|
||||
BinaryOp::Addition => Value::Number(
|
||||
left.evaluate(environment).value() + right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::Subtraction => Value::Number(
|
||||
left.evaluate(environment).value() - right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::Multiplication => Value::Number(
|
||||
left.evaluate(environment).value() * right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::Division => Value::Number(
|
||||
left.evaluate(environment).value() / right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::Modulo => Value::Number(
|
||||
left.evaluate(environment).value() % right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::Equal => match (left.evaluate(environment), right.evaluate(environment)) {
|
||||
(Value::Number(l), Value::Number(r)) => Value::Boolean(l == r),
|
||||
(Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l == r),
|
||||
_ => panic!("Cannot compare different types"),
|
||||
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::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()),
|
||||
},
|
||||
BinaryOp::NotEqual => match (left.evaluate(environment), right.evaluate(environment)) {
|
||||
(Value::Number(l), Value::Number(r)) => Value::Boolean(l != r),
|
||||
(Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l != r),
|
||||
_ => panic!("Cannot compare different types"),
|
||||
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()),
|
||||
},
|
||||
BinaryOp::GreaterEqual => Value::Boolean(
|
||||
left.evaluate(environment).value() >= right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::GreaterThan => Value::Boolean(
|
||||
left.evaluate(environment).value() > right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::LesserEqual => Value::Boolean(
|
||||
left.evaluate(environment).value() <= right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::LesserThan => Value::Boolean(
|
||||
left.evaluate(environment).value() < right.evaluate(environment).value(),
|
||||
),
|
||||
BinaryOp::And => Value::Boolean(
|
||||
left.evaluate(environment).truth() && right.evaluate(environment).truth(),
|
||||
),
|
||||
BinaryOp::Or => Value::Boolean(
|
||||
left.evaluate(environment).truth() || right.evaluate(environment).truth(),
|
||||
),
|
||||
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()?,
|
||||
)),
|
||||
BinaryOp::Or => Ok(Value::Boolean(
|
||||
left.evaluate(env)?.truth()? || right.evaluate(env)?.truth()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use ducklang::interpreter::machine::Machine;
|
||||
use ducklang::parser::ast::build_ast;
|
||||
use std::{env, fs::File, io::Read};
|
||||
use std::{env, fs::File, io::Read, process::exit};
|
||||
|
||||
// better cli
|
||||
// --debug flag
|
||||
pub fn main() {
|
||||
for arg in env::args().skip(1) {
|
||||
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)
|
||||
.unwrap_or_else(|_| panic!("Error in reading file {}", arg));
|
||||
let ast = build_ast(&content);
|
||||
let mut machine = Machine::new();
|
||||
machine.run(ast);
|
||||
}
|
||||
let path = env::args().nth(1).expect("no file provided");
|
||||
let mut f = File::open(&path).unwrap_or_else(|_| panic!("file {} not found", path));
|
||||
let mut content = String::new();
|
||||
f.read_to_string(&mut content)
|
||||
.unwrap_or_else(|_| panic!("Error in reading file {}", path));
|
||||
let ast = build_ast(&content).unwrap();
|
||||
let mut machine = Machine::new();
|
||||
let ret = machine.run(ast).expect("execution failed");
|
||||
exit(ret as i32);
|
||||
}
|
||||
|
|
|
@ -1,45 +1,85 @@
|
|||
use std::fmt::{Display, Formatter, Result};
|
||||
use std::{
|
||||
fmt::{Display, Formatter, Result},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::parser::syntax::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Environment {
|
||||
pub vars: HashMap<String, Value>,
|
||||
pub trait Environment {
|
||||
fn add(&mut self, name: &str, node: Value);
|
||||
fn get(&self, name: &str) -> Option<Value>;
|
||||
fn share_global(&mut self) -> &mut HashMap<String, Value>;
|
||||
}
|
||||
|
||||
impl Default for Environment {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct GlobalScope {
|
||||
pub global: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new() -> Environment {
|
||||
Environment {
|
||||
vars: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: &str, node: Value) {
|
||||
self.vars.insert(name.to_string(), node);
|
||||
}
|
||||
|
||||
pub fn get(&mut self, name: &str) -> Value {
|
||||
match self.vars.get(name) {
|
||||
Some(value) => *value,
|
||||
None => panic!("Variable {} not found", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Environment {
|
||||
impl Display for GlobalScope {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
let vars: Vec<String> = self
|
||||
.vars
|
||||
writeln!(f, "global: ")?;
|
||||
let global_vars: Vec<String> = self
|
||||
.global
|
||||
.iter()
|
||||
.map(|(key, val)| format!("var: {0} => val: {1}", key, val))
|
||||
.collect();
|
||||
write!(f, "{}", vars.join("\n"))
|
||||
writeln!(f, "{}", global_vars.join("\n"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment for GlobalScope {
|
||||
fn add(&mut self, name: &str, node: Value) {
|
||||
self.global.insert(name.to_string(), node);
|
||||
}
|
||||
|
||||
fn get(&self, name: &str) -> Option<Value> {
|
||||
self.global.get(name).cloned()
|
||||
}
|
||||
|
||||
fn share_global(&mut self) -> &mut HashMap<String, Value> {
|
||||
&mut self.global
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalScope<'a> {
|
||||
pub global: &'a mut HashMap<String, Value>,
|
||||
pub vars: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl LocalScope<'_> {
|
||||
pub fn new<'a>(
|
||||
env: &'a mut (dyn Environment + 'a),
|
||||
arg_names: &Rc<Vec<String>>,
|
||||
args: &[Value],
|
||||
) -> LocalScope<'a> {
|
||||
let mut vars = HashMap::new();
|
||||
for (arg_name, arg) in arg_names.iter().zip(args.iter()) {
|
||||
vars.insert(arg_name.clone(), arg.clone());
|
||||
}
|
||||
LocalScope {
|
||||
global: env.share_global(),
|
||||
vars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Environment for LocalScope<'a> {
|
||||
fn add(&mut self, name: &str, node: Value) {
|
||||
self.vars.insert(name.to_string(), node);
|
||||
}
|
||||
|
||||
fn get(&self, name: &str) -> Option<Value> {
|
||||
match self.vars.get(name) {
|
||||
Some(value) => Some(value.clone()),
|
||||
None => self.global.get(name).cloned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn share_global(&mut self) -> &mut HashMap<String, Value> {
|
||||
self.global
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,52 @@
|
|||
use super::{
|
||||
compute::{BinaryCompute, UnaryCompute},
|
||||
environment::Environment,
|
||||
environment::{Environment, LocalScope},
|
||||
machine::{ControlFlow, Execution},
|
||||
};
|
||||
use crate::parser::syntax::{Expr, Value};
|
||||
|
||||
pub trait Evaluate {
|
||||
fn evaluate(&self, environment: &mut Environment) -> Value;
|
||||
fn evaluate(&self, env: &mut dyn Environment) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
impl Evaluate for Expr {
|
||||
fn evaluate(&self, environment: &mut Environment) -> Value {
|
||||
fn evaluate(&self, env: &mut dyn Environment) -> Result<Value, String> {
|
||||
match *self {
|
||||
Expr::Litteral(ref value) => *value,
|
||||
Expr::Variable(ref name) => environment.get(name),
|
||||
Expr::UnaryOp(ref term, ref op) => op.compute(environment, term),
|
||||
Expr::BinaryOp(ref lhs, ref rhs, ref op) => op.compute(environment, lhs, rhs),
|
||||
Expr::Litteral(ref value) => Ok(value.clone()),
|
||||
Expr::Variable(ref name) => match env.get(name) {
|
||||
Some(value) => Ok(value.clone()),
|
||||
None => Err(format!("Variable {} not found", name)),
|
||||
},
|
||||
Expr::UnaryOp(ref term, ref op) => op.compute(env, term),
|
||||
Expr::BinaryOp(ref lhs, ref rhs, ref op) => op.compute(env, lhs, rhs),
|
||||
Expr::Call(ref name, ref args) => {
|
||||
let function = env
|
||||
.get(name)
|
||||
.ok_or_else(|| format!("function {} not found", name))?;
|
||||
match function {
|
||||
Value::Function(ref arg_names, ref body) => {
|
||||
let args =
|
||||
args.iter()
|
||||
.map(|arg| arg.evaluate(env))
|
||||
.collect::<Result<Vec<Value>, String>>()?;
|
||||
if arg_names.len() != args.len() {
|
||||
return Err(format!(
|
||||
"function {} takes {} arguments, found {}",
|
||||
name,
|
||||
arg_names.len(),
|
||||
args.len()
|
||||
));
|
||||
}
|
||||
let mut fn_env = LocalScope::new(env, arg_names, &args);
|
||||
if let ControlFlow::Return(value) = body.execute(&mut fn_env)? {
|
||||
Ok(value)
|
||||
} else {
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
_ => Err(format!("{} is not a function", name)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,57 +1,74 @@
|
|||
use super::{environment::Environment, evaluate::Evaluate};
|
||||
use crate::parser::syntax::Stat;
|
||||
use super::{
|
||||
environment::{Environment, GlobalScope},
|
||||
evaluate::Evaluate,
|
||||
};
|
||||
use crate::parser::syntax::{Stat, Value};
|
||||
|
||||
// can be used to add continue/exit
|
||||
pub enum ControlFlow {
|
||||
Next,
|
||||
Break,
|
||||
Continue,
|
||||
Return(Value),
|
||||
}
|
||||
|
||||
pub trait Execution {
|
||||
fn execute(&self, environment: &mut Environment) -> ControlFlow;
|
||||
fn execute(&self, environment: &mut dyn Environment) -> Result<ControlFlow, String>;
|
||||
}
|
||||
|
||||
impl Execution for Vec<Stat> {
|
||||
fn execute(&self, environment: &mut dyn Environment) -> Result<ControlFlow, String> {
|
||||
for statement in self {
|
||||
match statement.execute(environment)? {
|
||||
ControlFlow::Next => (),
|
||||
flow @ (ControlFlow::Break | ControlFlow::Continue) => return Ok(flow),
|
||||
ControlFlow::Return(value) => return Ok(ControlFlow::Return(value)),
|
||||
}
|
||||
}
|
||||
Ok(ControlFlow::Next)
|
||||
}
|
||||
}
|
||||
|
||||
impl Execution for Stat {
|
||||
fn execute(&self, environment: &mut Environment) -> ControlFlow {
|
||||
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() {
|
||||
for statement in consequence {
|
||||
if let ControlFlow::Break = statement.execute(environment) {
|
||||
return ControlFlow::Break;
|
||||
}
|
||||
}
|
||||
if condition.evaluate(environment)?.truth()? {
|
||||
return consequence.execute(environment);
|
||||
} else if let Some(statements) = alternative {
|
||||
for statement in statements {
|
||||
if let ControlFlow::Break = statement.execute(environment) {
|
||||
return ControlFlow::Break;
|
||||
}
|
||||
}
|
||||
return statements.execute(environment);
|
||||
}
|
||||
ControlFlow::Next
|
||||
Ok(ControlFlow::Next)
|
||||
}
|
||||
Stat::Loop(ref cond, ref body) => {
|
||||
while cond.evaluate(environment).truth() {
|
||||
for statement in body {
|
||||
if let ControlFlow::Break = statement.execute(environment) {
|
||||
return ControlFlow::Next;
|
||||
}
|
||||
while cond.evaluate(environment)?.truth()? {
|
||||
if let ControlFlow::Break = body.execute(environment)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ControlFlow::Next
|
||||
Ok(ControlFlow::Next)
|
||||
}
|
||||
Stat::Assignment(ref name, ref expr) => {
|
||||
let value = expr.evaluate(environment);
|
||||
let value = expr.evaluate(environment)?;
|
||||
environment.add(name, value);
|
||||
ControlFlow::Next
|
||||
// println!("environment:\n{}", environment);
|
||||
Ok(ControlFlow::Next)
|
||||
}
|
||||
Stat::Break => ControlFlow::Break,
|
||||
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)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
pub environment: Environment,
|
||||
pub environment: GlobalScope,
|
||||
}
|
||||
|
||||
impl Default for Machine {
|
||||
|
@ -63,18 +80,20 @@ impl Default for Machine {
|
|||
impl Machine {
|
||||
pub fn new() -> Machine {
|
||||
Machine {
|
||||
environment: Environment::default(),
|
||||
environment: GlobalScope::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_env(environment: Environment) -> Machine {
|
||||
pub fn new_with_env(environment: GlobalScope) -> Machine {
|
||||
Machine { environment }
|
||||
}
|
||||
|
||||
pub fn run(&mut self, statements: Vec<Stat>) {
|
||||
pub fn run(&mut self, statements: Vec<Stat>) -> Result<i64, String> {
|
||||
for statement in statements {
|
||||
statement.execute(&mut self.environment);
|
||||
if let ControlFlow::Return(val) = statement.execute(&mut self.environment)? {
|
||||
return val.value();
|
||||
}
|
||||
}
|
||||
println!("Environment state:\n{}", self.environment);
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,30 +8,33 @@ mod tests {
|
|||
use crate::parser::{ast::build_ast, syntax::Value};
|
||||
use std::{fs::File, io::Read};
|
||||
|
||||
use super::{environment::Environment, machine::Machine};
|
||||
use super::{
|
||||
environment::{Environment, GlobalScope},
|
||||
machine::Machine,
|
||||
};
|
||||
|
||||
// modify to inclue in main
|
||||
fn run_file(path: &str) -> Environment {
|
||||
fn run_file(path: &str) -> GlobalScope {
|
||||
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 ast = build_ast(&content).unwrap();
|
||||
let mut machine = Machine::new();
|
||||
machine.run(ast);
|
||||
machine.run(ast).expect("execution failed");
|
||||
machine.environment
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_assign() {
|
||||
let mut environment = run_file("test_programs/assign.duck");
|
||||
let environment = run_file("test_programs/assign.duck");
|
||||
let x = match environment.get("x") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("x should be Value::Boolean"),
|
||||
};
|
||||
assert!(x == true);
|
||||
let y = match environment.get("y") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("y should be Value::Number"),
|
||||
};
|
||||
assert!(y == 1);
|
||||
|
@ -39,19 +42,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_arithmetic_expr() {
|
||||
let mut environment = run_file("test_programs/arithmetic_expr.duck");
|
||||
let environment = run_file("test_programs/arithmetic_expr.duck");
|
||||
let x = match environment.get("x") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("x should be Value::Number"),
|
||||
};
|
||||
assert!(x == 2);
|
||||
let y = match environment.get("y") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("y should be Value::Number"),
|
||||
};
|
||||
assert!(y == 3 + 4 * (x + 1));
|
||||
let z = match environment.get("z") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("z should be Value::Number"),
|
||||
};
|
||||
assert!(z == x % (-3 / 2));
|
||||
|
@ -59,54 +62,54 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_boolean_expr() {
|
||||
let mut environment = run_file("test_programs/boolean_expr.duck");
|
||||
let environment = run_file("test_programs/boolean_expr.duck");
|
||||
let a = match environment.get("a") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("a should be Value::Boolean"),
|
||||
};
|
||||
assert!(a == (false || false));
|
||||
let b = match environment.get("b") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("b should be Value::Boolean"),
|
||||
};
|
||||
assert!(b == (true || false));
|
||||
let c = match environment.get("c") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("c should be Value::Boolean"),
|
||||
};
|
||||
assert!(c == (false || true));
|
||||
let d = match environment.get("d") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("d should be Value::Boolean"),
|
||||
};
|
||||
assert!(d == (true || true));
|
||||
let e = match environment.get("e") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("e should be Value::Boolean"),
|
||||
};
|
||||
assert!(e == (false && false));
|
||||
let f = match environment.get("f") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("f should be Value::Boolean"),
|
||||
};
|
||||
assert!(f == (true && false));
|
||||
let g = match environment.get("g") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("g should be Value::Boolean"),
|
||||
};
|
||||
assert!(g == (false && true));
|
||||
let h = match environment.get("h") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("h should be Value::Boolean"),
|
||||
};
|
||||
assert!(h == (true && true));
|
||||
let i = match environment.get("i") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("i should be Value::Boolean"),
|
||||
};
|
||||
assert!(i == !true);
|
||||
let j = match environment.get("j") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("j should be Value::Boolean"),
|
||||
};
|
||||
assert!(j == !false);
|
||||
|
@ -114,84 +117,84 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_compare_expr() {
|
||||
let mut environment = run_file("test_programs/compare_expr.duck");
|
||||
let environment = run_file("test_programs/compare_expr.duck");
|
||||
let a = match environment.get("a") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("a should be Value::Boolean"),
|
||||
};
|
||||
assert!(a == (1 < 2));
|
||||
let b = match environment.get("b") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("b should be Value::Boolean"),
|
||||
};
|
||||
assert!(b == (2 < 1));
|
||||
let c = match environment.get("c") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("c should be Value::Boolean"),
|
||||
};
|
||||
assert!(c == (2 < 2));
|
||||
let d = match environment.get("d") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("d should be Value::Boolean"),
|
||||
};
|
||||
assert!(d == (1 > 2));
|
||||
let e = match environment.get("e") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("e should be Value::Boolean"),
|
||||
};
|
||||
assert!(e == (2 > 1));
|
||||
let f = match environment.get("f") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("f should be Value::Boolean"),
|
||||
};
|
||||
assert!(f == (2 > 2));
|
||||
let g = match environment.get("g") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("g should be Value::Boolean"),
|
||||
};
|
||||
assert!(g == (1 <= 2));
|
||||
let h = match environment.get("h") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("h should be Value::Boolean"),
|
||||
};
|
||||
assert!(h == (2 <= 1));
|
||||
let i = match environment.get("i") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("i should be Value::Boolean"),
|
||||
};
|
||||
assert!(i == (2 <= 2));
|
||||
let j = match environment.get("j") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("j should be Value::Boolean"),
|
||||
};
|
||||
assert!(j == (1 >= 2));
|
||||
let k = match environment.get("k") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("k should be Value::Boolean"),
|
||||
};
|
||||
assert!(k == (2 >= 1));
|
||||
let l = match environment.get("l") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("l should be Value::Boolean"),
|
||||
};
|
||||
assert!(l == (2 >= 2));
|
||||
let m = match environment.get("m") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("m should be Value::Boolean"),
|
||||
};
|
||||
assert!(m == (1 == 2));
|
||||
let n = match environment.get("n") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("n should be Value::Boolean"),
|
||||
};
|
||||
assert!(n == (2 == 2));
|
||||
let o = match environment.get("o") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("o should be Value::Boolean"),
|
||||
};
|
||||
assert!(o == (1 != 2));
|
||||
let p = match environment.get("p") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("p should be Value::Boolean"),
|
||||
};
|
||||
assert!(p == (2 != 2));
|
||||
|
@ -199,29 +202,29 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_conditional() {
|
||||
let mut environment = run_file("test_programs/conditional.duck");
|
||||
let environment = run_file("test_programs/conditional.duck");
|
||||
let x = match environment.get("x") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("x should be Value::Boolean"),
|
||||
};
|
||||
assert!(x == true);
|
||||
let y = match environment.get("y") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("y should be Value::Boolean"),
|
||||
};
|
||||
assert!(y == false);
|
||||
let z = match environment.get("z") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("z should be Value::Boolean"),
|
||||
};
|
||||
assert!(z == true);
|
||||
let a = match environment.get("a") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("a should be Value::Boolean"),
|
||||
};
|
||||
assert!(a == true);
|
||||
let b = match environment.get("b") {
|
||||
Value::Boolean(value) => value,
|
||||
Some(Value::Boolean(value)) => value,
|
||||
_ => panic!("b should be Value::Boolean"),
|
||||
};
|
||||
assert!(b == true);
|
||||
|
@ -229,16 +232,41 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_while_loop() {
|
||||
let mut environment = run_file("test_programs/while_loop.duck");
|
||||
let environment = run_file("test_programs/while_loop.duck");
|
||||
let x = match environment.get("x") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("x should be Value::Number"),
|
||||
};
|
||||
assert!(x == 5);
|
||||
let y = match environment.get("y") {
|
||||
Value::Number(value) => value,
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("y should be Value::Number"),
|
||||
};
|
||||
assert!(y == 5);
|
||||
let z = match environment.get("z") {
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("z should be Value::Number"),
|
||||
};
|
||||
assert!(z == 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_functions() {
|
||||
let environment = run_file("test_programs/functions.duck");
|
||||
let x = match environment.get("x") {
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("x should be Value::Number"),
|
||||
};
|
||||
assert!(x == 6765);
|
||||
let y = match environment.get("y") {
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("y should be Value::Number"),
|
||||
};
|
||||
assert!(y == 2);
|
||||
let z = match environment.get("z") {
|
||||
Some(Value::Number(value)) => value,
|
||||
_ => panic!("z should be Value::Number"),
|
||||
};
|
||||
assert!(z == 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,108 +47,199 @@ fn parsing_error(pair: Pair<Rule>, error: &str) -> String {
|
|||
error_print.join("\n")
|
||||
}
|
||||
|
||||
fn build_expr(pair: Pair<Rule>) -> Box<Expr> {
|
||||
// println!("build_expr::{:#?}", pair);
|
||||
fn build_call(pair: Pair<Rule>) -> Result<Expr, String> {
|
||||
let mut inner = pair.into_inner();
|
||||
let name = inner.next().unwrap().as_str().to_string();
|
||||
let args = inner.next().unwrap().into_inner();
|
||||
let arg_exprs = args
|
||||
.into_iter()
|
||||
.map(|arg| build_expr(arg).unwrap())
|
||||
.collect();
|
||||
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 => Expr::litteral(Value::number(primary.as_str())),
|
||||
Rule::boolean => Expr::litteral(Value::boolean(primary.as_str())),
|
||||
Rule::variable => Expr::variable(primary.as_str()),
|
||||
rule => unreachable!("SimpleParser::climb expected atom, found {:?}", rule),
|
||||
})
|
||||
.map_infix(|lhs, op, rhs| match op.as_rule() {
|
||||
Rule::op_add => Expr::binary_op(lhs, rhs, BinaryOp::Addition),
|
||||
Rule::op_sub => Expr::binary_op(lhs, rhs, BinaryOp::Subtraction),
|
||||
Rule::op_mul => Expr::binary_op(lhs, rhs, BinaryOp::Multiplication),
|
||||
Rule::op_div => Expr::binary_op(lhs, rhs, BinaryOp::Division),
|
||||
Rule::op_mod => Expr::binary_op(lhs, rhs, BinaryOp::Modulo),
|
||||
Rule::op_lt => Expr::binary_op(lhs, rhs, BinaryOp::LesserThan),
|
||||
Rule::op_gt => Expr::binary_op(lhs, rhs, BinaryOp::GreaterThan),
|
||||
Rule::op_eq => Expr::binary_op(lhs, rhs, BinaryOp::Equal),
|
||||
Rule::op_ge => Expr::binary_op(lhs, rhs, BinaryOp::GreaterEqual),
|
||||
Rule::op_le => Expr::binary_op(lhs, rhs, BinaryOp::LesserEqual),
|
||||
Rule::op_ne => Expr::binary_op(lhs, rhs, BinaryOp::NotEqual),
|
||||
Rule::op_or => Expr::binary_op(lhs, rhs, BinaryOp::Or),
|
||||
Rule::op_and => Expr::binary_op(lhs, rhs, BinaryOp::And),
|
||||
rule => unreachable!(
|
||||
"SimpleParser::climb expected infix operation, found {:?}",
|
||||
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::call => build_call(primary),
|
||||
rule => Err(format!(
|
||||
"atom expected number, boolean or variable, found {:?}",
|
||||
rule
|
||||
),
|
||||
)),
|
||||
})
|
||||
.map_prefix(|op, rhs| match op.as_rule() {
|
||||
Rule::op_neg => Expr::unary_op(rhs, UnaryOp::Minus),
|
||||
Rule::op_not => Expr::unary_op(rhs, UnaryOp::Not),
|
||||
rule => unreachable!(
|
||||
"SimpleParser::climb expected prefix operation, found {:?}",
|
||||
.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(lhs), Rule::op_and, Ok(rhs)) => {
|
||||
Ok(Expr::binary_op(Box::new(lhs), Box::new(rhs), BinaryOp::And))
|
||||
}
|
||||
(Ok(lhs), Rule::op_lt, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::LesserThan,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_gt, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::GreaterThan,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_eq, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Equal,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_ge, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::GreaterEqual,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_le, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::LesserEqual,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_ne, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::NotEqual,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_add, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Addition,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_sub, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Subtraction,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_mul, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Multiplication,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_div, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Division,
|
||||
)),
|
||||
(Ok(lhs), Rule::op_mod, Ok(rhs)) => Ok(Expr::binary_op(
|
||||
Box::new(lhs),
|
||||
Box::new(rhs),
|
||||
BinaryOp::Modulo,
|
||||
)),
|
||||
(_, rule, _) => Err(format!(
|
||||
"binary operation expected operator, found {:?}",
|
||||
rule
|
||||
),
|
||||
)),
|
||||
})
|
||||
.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, _) => Err(format!(
|
||||
"unary operation expected operator, found {:?}",
|
||||
rule
|
||||
)),
|
||||
})
|
||||
.parse(pair.into_inner())
|
||||
}
|
||||
|
||||
fn build_assign(pair: Pair<Rule>) -> Box<Stat> {
|
||||
// println!("build_assign::{:#?}", pair);
|
||||
fn build_assign(pair: Pair<Rule>) -> Result<Stat, String> {
|
||||
let mut inner = pair.into_inner();
|
||||
let lhs = inner.next().unwrap().as_str();
|
||||
let rhs = build_expr(inner.next().unwrap());
|
||||
Stat::assign(lhs, rhs)
|
||||
let rhs = build_expr(inner.next().unwrap())?;
|
||||
Ok(Stat::assign(lhs, rhs))
|
||||
}
|
||||
|
||||
fn build_while_loop(pair: Pair<Rule>) -> Box<Stat> {
|
||||
// println!("build_while_loop::{:#?}", pair);
|
||||
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);
|
||||
Stat::while_loop(cond, stmt)
|
||||
let cond = build_expr(inner.next().unwrap())?;
|
||||
let stmt = build_stats(inner.next().unwrap(), true)?;
|
||||
Ok(Stat::while_loop(cond, stmt))
|
||||
}
|
||||
|
||||
fn build_if_cond(pair: Pair<Rule>, in_loop: bool) -> Box<Stat> {
|
||||
//println!("build_if_cond::{:#?}", pair);
|
||||
fn build_if_cond(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
|
||||
let mut inner = pair.into_inner();
|
||||
let cond = build_expr(inner.next().unwrap());
|
||||
let thenstmt = build_stats(inner.next().unwrap(), in_loop);
|
||||
let cond = build_expr(inner.next().unwrap())?;
|
||||
let thenstmt = build_stats(inner.next().unwrap(), in_loop)?;
|
||||
match inner.next() {
|
||||
Some(stmt) => match stmt.as_rule() {
|
||||
Rule::stats => Stat::if_cond(cond, thenstmt, Some(build_stats(stmt, in_loop))),
|
||||
Rule::stat_elif => {
|
||||
Stat::if_cond(cond, thenstmt, Some(vec![*build_stat(stmt, in_loop)]))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
Rule::stats => Ok(Stat::if_cond(
|
||||
cond,
|
||||
thenstmt,
|
||||
Some(build_stats(stmt, in_loop)?),
|
||||
)),
|
||||
Rule::stat_elif => Ok(Stat::if_cond(
|
||||
cond,
|
||||
thenstmt,
|
||||
Some(vec![build_stat(stmt, in_loop)?]),
|
||||
)),
|
||||
rule => Err(format!("conditional statement expected, found {:?}", rule)),
|
||||
},
|
||||
None => Stat::if_cond(cond, thenstmt, None),
|
||||
None => Ok(Stat::if_cond(cond, thenstmt, None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Box<Stat> {
|
||||
// println!("build_stat: {:#?}", pair);
|
||||
fn build_define(pair: Pair<Rule>) -> Result<Stat, String> {
|
||||
let mut inner = pair.into_inner();
|
||||
let name = inner.next().unwrap().as_str().to_string();
|
||||
let args = inner.next().unwrap().into_inner();
|
||||
let arg_names = args
|
||||
.into_iter()
|
||||
.map(|arg| arg.as_str().to_string())
|
||||
.collect();
|
||||
let stmt = build_stats(inner.next().unwrap(), false)?;
|
||||
Ok(Stat::define(name, arg_names, stmt))
|
||||
}
|
||||
|
||||
fn build_return(pair: Pair<Rule>) -> Result<Stat, String> {
|
||||
let expr = build_expr(pair.into_inner().next().unwrap())?;
|
||||
Ok(Stat::return_keyword(expr))
|
||||
}
|
||||
|
||||
fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
|
||||
match pair.as_rule() {
|
||||
Rule::stat_assign => build_assign(pair),
|
||||
Rule::stat_if | Rule::stat_elif => build_if_cond(pair, in_loop),
|
||||
Rule::stat_while => build_while_loop(pair),
|
||||
Rule::stat_break => {
|
||||
if in_loop {
|
||||
return Stat::break_keyword();
|
||||
return Ok(Stat::break_keyword());
|
||||
}
|
||||
panic!("{}", parsing_error(pair, "break keyword while not in loop"))
|
||||
Err(parsing_error(pair, "break keyword while not in loop"))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
Rule::stat_continue => {
|
||||
if in_loop {
|
||||
return Ok(Stat::continue_keyword());
|
||||
}
|
||||
Err(parsing_error(pair, "continue keyword while not in loop"))
|
||||
}
|
||||
Rule::stat_define => build_define(pair),
|
||||
Rule::stat_return => build_return(pair),
|
||||
rule => Err(format!("Statement expected, found {:?}", rule)),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_stats(pair: Pair<Rule>, in_loop: bool) -> Vec<Stat> {
|
||||
// println!("build_stats::{:#?}", pair);
|
||||
fn build_stats(pair: Pair<Rule>, in_loop: bool) -> Result<Vec<Stat>, String> {
|
||||
let inner = pair.into_inner();
|
||||
inner
|
||||
.into_iter()
|
||||
.map(|pair| *build_stat(pair, in_loop))
|
||||
.collect()
|
||||
let mut stats = Vec::new();
|
||||
for pair in inner {
|
||||
stats.push(build_stat(pair, in_loop)?);
|
||||
}
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub fn build_ast(content: &str) -> Vec<Stat> {
|
||||
let pair = DuckParser::parse(Rule::program, content)
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
.next()
|
||||
.unwrap();
|
||||
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)),
|
||||
};
|
||||
build_stats(pair, false)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ COMMENT = _{ block_comment | line_comment }
|
|||
line_comment = @{ "//" ~ (!("\r" | "\n") ~ ANY)* ~ ("\n" | "\r\n" | "\r" | EOI) }
|
||||
block_comment = @{ "/*" ~ ((!("*/") ~ ANY) | block_comment)* ~ "*/" }
|
||||
|
||||
args_call = { (expr ~ ("," ~ expr)*)? }
|
||||
call = { variable ~ "(" ~ args_call ~ ")" }
|
||||
variable = @ { (alpha) ~ (alpha | digit)* }
|
||||
number = @ { (digit)+ }
|
||||
boolean = @ { "true" | "false" }
|
||||
|
@ -46,10 +48,15 @@ op_unary = _{
|
|||
op_not
|
||||
}
|
||||
|
||||
value = _{ boolean | number | variable | "(" ~ expr ~ ")" }
|
||||
value = _{ call | boolean | number | variable | "(" ~ expr ~ ")" }
|
||||
term = { op_unary* ~ value }
|
||||
expr = { term ~ (op_binary ~ term)* }
|
||||
args_define = { (variable ~ ("," ~ variable)*)? }
|
||||
|
||||
|
||||
stat_return = { "return" ~ expr ~ ";" }
|
||||
stat_define = { "fn" ~ variable ~ "(" ~ args_define ~ ")" ~ "{" ~ stats ~ "}" }
|
||||
stat_continue = { "continue" ~ ";" }
|
||||
stat_break = { "break" ~ ";" }
|
||||
stat_assign = { variable ~ "=" ~ expr ~ ";" }
|
||||
stat_while = { "while" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" }
|
||||
|
@ -58,7 +65,7 @@ stat_elif = { ("else if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ "else" ~ "{" ~
|
|||
stat_if = { ("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ "else" ~ "{" ~ stats ~ "}" ) |
|
||||
("if" ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ (stat_elif)?) }
|
||||
|
||||
stat = _{ ( stat_if | stat_while | stat_assign | stat_break ) }
|
||||
stat = _{ ( stat_if | stat_while | stat_assign | stat_break | stat_continue | stat_define | stat_return ) }
|
||||
|
||||
stats = { (stat)* }
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ pub fn main() {
|
|||
let mut content = String::new();
|
||||
f.read_to_string(&mut content)
|
||||
.unwrap_or_else(|_| panic!("Error in reading file {}", arg));
|
||||
build_ast(&content);
|
||||
build_ast(&content).expect("parsing failed");
|
||||
println!("{} is a valid ducklang program", arg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ mod tests {
|
|||
let mut content = String::new();
|
||||
f.read_to_string(&mut content)
|
||||
.expect(&format!("Error in reading file {}", path));
|
||||
build_ast(&content);
|
||||
build_ast(&content).unwrap();
|
||||
}
|
||||
|
||||
// can loop with some tests libs ?
|
||||
|
@ -51,4 +51,9 @@ mod tests {
|
|||
fn test_break_not_loop() {
|
||||
parse_file("test_programs/break_not_loop.duck");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_functions() {
|
||||
parse_file("test_programs/functions.duck");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
#![allow(clippy::should_implement_trait)]
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
// REFACTORING
|
||||
// TODO:
|
||||
// - better op name
|
||||
// - split in multiple files
|
||||
// - unbox stat and expr ?
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BinaryOp {
|
||||
Addition,
|
||||
Subtraction,
|
||||
|
@ -24,7 +22,7 @@ pub enum BinaryOp {
|
|||
}
|
||||
|
||||
impl Display for BinaryOp {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
BinaryOp::Addition => write!(f, "+"),
|
||||
BinaryOp::Subtraction => write!(f, "-"),
|
||||
|
@ -43,13 +41,14 @@ impl Display for BinaryOp {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum UnaryOp {
|
||||
Minus,
|
||||
Not,
|
||||
}
|
||||
|
||||
impl Display for UnaryOp {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UnaryOp::Minus => write!(f, "-"),
|
||||
UnaryOp::Not => write!(f, "!"),
|
||||
|
@ -57,113 +56,146 @@ impl Display for UnaryOp {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
// Rc all the heavy stuff
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Value {
|
||||
Number(i64),
|
||||
Boolean(bool),
|
||||
Function(Rc<Vec<String>>, Rc<Vec<Stat>>),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn number(raw: &str) -> Value {
|
||||
Value::Number(raw.parse::<i64>().unwrap())
|
||||
}
|
||||
pub fn boolean(raw: &str) -> Value {
|
||||
Value::Boolean(raw.parse::<bool>().unwrap())
|
||||
}
|
||||
pub fn value(&self) -> i64 {
|
||||
match self {
|
||||
Value::Number(n) => *n,
|
||||
_ => panic!("Value::as_number: not a number"),
|
||||
pub fn number(raw: &str) -> Result<Value, String> {
|
||||
match raw.parse::<i64>() {
|
||||
Ok(value) => Ok(Value::Number(value)),
|
||||
Err(_) => Err(format!("{} is not a number", raw)),
|
||||
}
|
||||
}
|
||||
pub fn truth(&self) -> bool {
|
||||
pub fn boolean(raw: &str) -> Result<Value, String> {
|
||||
match raw.parse::<bool>() {
|
||||
Ok(value) => Ok(Value::Boolean(value)),
|
||||
Err(_) => Err(format!("{} is not a boolean", raw)),
|
||||
}
|
||||
}
|
||||
// TODO move to compute.rs
|
||||
pub fn value(&self) -> Result<i64, String> {
|
||||
match self {
|
||||
Value::Boolean(b) => *b,
|
||||
Value::Number(value) => *value != 0,
|
||||
// _ => panic!("Value::as_boolean: not a boolean"),
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::Number(n) => write!(f, "{}", n),
|
||||
Value::Boolean(b) => write!(f, "{}", b),
|
||||
Value::Function(args, _) => write!(f, "fn({})", args.join(", ")),
|
||||
Value::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// box because of recursive definition
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expr {
|
||||
Litteral(Value),
|
||||
Variable(String),
|
||||
BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
|
||||
UnaryOp(Box<Expr>, UnaryOp),
|
||||
Call(String, Vec<Expr>),
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn litteral(value: Value) -> Box<Expr> {
|
||||
Box::new(Expr::Litteral(value))
|
||||
pub fn litteral(value: Value) -> Expr {
|
||||
Expr::Litteral(value)
|
||||
}
|
||||
pub fn variable(name: &str) -> Box<Expr> {
|
||||
Box::new(Expr::Variable(name.to_string()))
|
||||
pub fn variable(name: &str) -> Expr {
|
||||
Expr::Variable(name.to_string())
|
||||
}
|
||||
pub fn binary_op(left: Box<Expr>, right: Box<Expr>, op: BinaryOp) -> Box<Expr> {
|
||||
Box::new(Expr::BinaryOp(left, right, op))
|
||||
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) -> Box<Expr> {
|
||||
Box::new(Expr::UnaryOp(term, 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) -> Result {
|
||||
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::Call(name, args) => {
|
||||
write!(f, "{}(", name)?;
|
||||
let expressions = args
|
||||
.iter()
|
||||
.map(|arg| arg.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
write!(f, "{}", expressions.join(", "))?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Stat {
|
||||
Condition(Box<Expr>, Vec<Stat>, Option<Vec<Stat>>),
|
||||
Loop(Box<Expr>, Vec<Stat>),
|
||||
Assignment(String, Box<Expr>),
|
||||
Condition(Expr, Vec<Stat>, Option<Vec<Stat>>),
|
||||
Loop(Expr, Vec<Stat>),
|
||||
Assignment(String, Expr),
|
||||
Break,
|
||||
// continue
|
||||
// declare
|
||||
// call
|
||||
// return
|
||||
Continue,
|
||||
Define(String, Rc<Vec<String>>, Rc<Vec<Stat>>),
|
||||
Return(Expr),
|
||||
}
|
||||
|
||||
impl Stat {
|
||||
pub fn assign(name: &str, expr: Box<Expr>) -> Box<Stat> {
|
||||
Box::new(Stat::Assignment(name.to_string(), expr))
|
||||
pub fn assign(name: &str, expr: Expr) -> Stat {
|
||||
Stat::Assignment(name.to_string(), expr)
|
||||
}
|
||||
pub fn break_keyword() -> Box<Stat> {
|
||||
Box::new(Stat::Break)
|
||||
pub fn break_keyword() -> Stat {
|
||||
Stat::Break
|
||||
}
|
||||
pub fn while_loop(condition: Box<Expr>, body: Vec<Stat>) -> Box<Stat> {
|
||||
Box::new(Stat::Loop(condition, body))
|
||||
pub fn continue_keyword() -> Stat {
|
||||
Stat::Continue
|
||||
}
|
||||
pub fn if_cond(
|
||||
condition: Box<Expr>,
|
||||
body: Vec<Stat>,
|
||||
else_body: Option<Vec<Stat>>,
|
||||
) -> Box<Stat> {
|
||||
Box::new(Stat::Condition(condition, body, else_body))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Stat {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Stat::Condition(condition, body, else_body) => {
|
||||
write!(f, "if {} {{", condition)?;
|
||||
write!(f, "if ({}) {{", condition)?;
|
||||
for statement in body {
|
||||
write!(f, "{}", statement)?;
|
||||
write!(f, " {} ", statement)?;
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
if let Some(else_body) = else_body {
|
||||
|
@ -176,7 +208,7 @@ impl Display for Stat {
|
|||
Ok(())
|
||||
}
|
||||
Stat::Loop(condition, body) => {
|
||||
write!(f, "while {} {{", condition)?;
|
||||
write!(f, "while ({}) {{", condition)?;
|
||||
for statement in body {
|
||||
write!(f, " {} ", statement)?;
|
||||
}
|
||||
|
@ -184,6 +216,17 @@ impl Display for Stat {
|
|||
}
|
||||
Stat::Assignment(name, expr) => write!(f, "{} = {};", name, expr),
|
||||
Stat::Break => write!(f, "break;"),
|
||||
Stat::Continue => write!(f, "continue;"),
|
||||
Stat::Define(name, args, body) => {
|
||||
write!(f, "fn {}( ", name)?;
|
||||
write!(f, "{}", args.join(", "))?;
|
||||
write!(f, " ) {{")?;
|
||||
for statement in body.as_ref() {
|
||||
write!(f, " {} ", statement)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Stat::Return(expr) => write!(f, "return {};", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
use ducklang::{interpreter::machine::Machine, parser::ast::build_ast};
|
||||
|
||||
const EXAMPLE_CODE: &str = r#"/*
|
||||
An example of program with most of
|
||||
the supported features
|
||||
*/
|
||||
x = true;
|
||||
z = 3 + 4 * (2 + 1); // good order of op
|
||||
if (x) {
|
||||
y = 5;
|
||||
while (x) {
|
||||
if (!y && z % 2) {
|
||||
break;
|
||||
} else if (y == 3) { // never reached because of continue
|
||||
z = 5;
|
||||
} else if (y == 4) {
|
||||
y = y - 2;
|
||||
continue;
|
||||
}
|
||||
y = y - 1;
|
||||
}
|
||||
} else {
|
||||
// never reached
|
||||
y = -8;
|
||||
x = true;
|
||||
}"#;
|
||||
|
||||
pub struct MyEguiApp {
|
||||
code: String,
|
||||
machine: Machine,
|
||||
error: String,
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
Self {
|
||||
code: String::from(EXAMPLE_CODE),
|
||||
machine: Machine::new(),
|
||||
error: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for MyEguiApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
let _toolbar = self.render_toolbar(ctx);
|
||||
let _side_panel = self.render_side_panel(ctx);
|
||||
let _error_panel = self.render_error_panel(ctx);
|
||||
let _central_panel = self.render_central_panel(ctx);
|
||||
// TODO
|
||||
// toolbar:
|
||||
// - reset code and env
|
||||
// - reset env
|
||||
// - run code
|
||||
// - ...
|
||||
// central panel:
|
||||
// - code editor
|
||||
// side panel:
|
||||
// - env
|
||||
}
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
fn render_error_panel(&mut self, ctx: &egui::Context) -> egui::Rect {
|
||||
let error_panel = egui::TopBottomPanel::bottom("error_panel").show(ctx, |ui| {
|
||||
ui.heading("Error");
|
||||
ui.separator();
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut self.error.as_str()).desired_width(f32::INFINITY),
|
||||
);
|
||||
});
|
||||
error_panel.response.rect
|
||||
}
|
||||
|
||||
fn render_central_panel(&mut self, ctx: &egui::Context) -> egui::Rect {
|
||||
let central_panel = egui::CentralPanel::default().show(ctx, |ui| {
|
||||
// calculate desired rows based on the height of the window
|
||||
let font_selection = egui::FontSelection::default();
|
||||
let font_id = font_selection.resolve(ui.style());
|
||||
let row_height = ui.fonts(|fonts| fonts.row_height(&font_id));
|
||||
let rows = (ui.available_size().y / row_height) as usize;
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut self.code)
|
||||
.code_editor()
|
||||
.desired_width(f32::INFINITY)
|
||||
.desired_rows(rows),
|
||||
);
|
||||
});
|
||||
central_panel.response.rect
|
||||
}
|
||||
|
||||
fn render_side_panel(&mut self, ctx: &egui::Context) -> egui::Rect {
|
||||
let side_panel = egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
||||
ui.heading("Environment");
|
||||
ui.separator();
|
||||
for (name, value) in self.machine.environment.global.iter() {
|
||||
ui.label(format!("{}: {}", name, value));
|
||||
}
|
||||
});
|
||||
side_panel.response.rect
|
||||
}
|
||||
|
||||
fn render_toolbar(&mut self, ctx: &egui::Context) -> egui::Rect {
|
||||
let mut frame = egui::Frame::side_top_panel(ctx.style().as_ref());
|
||||
frame.inner_margin.bottom += 1.5;
|
||||
let toolbar = egui::TopBottomPanel::top("toolbar")
|
||||
.frame(frame)
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.style_mut().spacing.interact_size.y *= 1.25;
|
||||
ui.style_mut()
|
||||
.text_styles
|
||||
.get_mut(&egui::TextStyle::Button)
|
||||
.unwrap()
|
||||
.size *= 1.25;
|
||||
if ui.button("Reset").clicked() {
|
||||
self.reset();
|
||||
}
|
||||
if ui.button("Run").clicked() {
|
||||
self.run();
|
||||
}
|
||||
});
|
||||
});
|
||||
toolbar.response.rect
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.machine = Machine::new();
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
match build_ast(&self.code) {
|
||||
Ok(ast) => match self.machine.run(ast) {
|
||||
Ok(_) => {
|
||||
self.error = String::new();
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = e;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
self.error = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
// Make sure panics are logged using `console.error`.
|
||||
console_error_panic_hook::set_once();
|
||||
tracing_wasm::set_as_global_default();
|
||||
|
||||
let web_options = eframe::WebOptions::default();
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
eframe::start_web(
|
||||
// this is the id of the `<canvas>` element we have
|
||||
// in our `index.html`
|
||||
"canvas",
|
||||
web_options,
|
||||
Box::new(|cc| Box::new(MyEguiApp::new(cc))),
|
||||
)
|
||||
.await
|
||||
.expect("failed to start eframe");
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([1280.0, 720.0])
|
||||
.with_resizable(true),
|
||||
..Default::default()
|
||||
};
|
||||
let _run_native = eframe::run_native(
|
||||
"Duckscript Playground",
|
||||
options,
|
||||
Box::new(|cc| Box::new(MyEguiApp::new(cc))),
|
||||
);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
fn add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
fn sub(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
fn do(f, n) {
|
||||
return f(n, n);
|
||||
}
|
||||
|
||||
fn fib(n) {
|
||||
if (n < 2) {
|
||||
return n;
|
||||
}
|
||||
return add(fib(sub(n, 1)), fib(sub(n, 2)));
|
||||
}
|
||||
|
||||
x = fib(20);
|
||||
y = do(add, 1);
|
||||
z = do(sub, 1);
|
||||
return y + z;
|
|
@ -1,8 +1,13 @@
|
|||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
|
||||
while (x < 5) {
|
||||
x = x + 1;
|
||||
if (x % 2) {
|
||||
continue;
|
||||
}
|
||||
z = z + 1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
|
Loading…
Reference in New Issue