feat(scope): local scope for blocks + better env print

main
flavien 2023-12-17 11:35:26 +01:00
parent 7ae18228a3
commit 76b364191a
No known key found for this signature in database
9 changed files with 135 additions and 69 deletions

View File

@ -4,22 +4,16 @@ Written in Rust with pest PEG parser.
<br>
### Currently implemented:
- number (i64), boolean
- number (i64), boolean, string, char and functions
- `+ - * / %` arithmetic operators
- `< > <= >= == != && || !` boolean operators
- variables
- `if else` conditional, `while` loop, `break` keyword
- `if else` conditional, `while` loop, `break` and `continue` keyword
- functions as variables
- indexing for string
<br>
### 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
```
@ -59,8 +53,7 @@ var: z => val: 8
- functions OK
- print statement OK
- string / char => indexing OK / escaping OK
- local scope for { statements } ?
- pow op ?
- local scope for { statements } OK
- array / map => iterators
- read / write
- types declaration
@ -68,10 +61,19 @@ var: z => val: 8
- types inference
- program arguments
- structs => methods (list container?)
- pow op ?
- range
- import from other files
- better tests / unit tests
- check and modify parsing error printing
- ast unit tests
- check and enhance parsing error printing
### playground
- compile time inclusion of the example program
- syntax color
- line numbers
- better panel title
- panel tabs
- print / string => language
- console for print
### tools
- repl
- LLVM IR translation

View File

@ -8,27 +8,18 @@ pub trait Environment {
fn create(&mut self, name: &str, node: Value) -> bool;
fn assign(&mut self, name: &str, node: Value) -> Option<Value>;
fn get(&self, name: &str) -> Option<Value>;
fn recursive_to_string(&self, level: usize) -> (String, usize);
fn to_string(&self) -> String {
let (result, _) = self.recursive_to_string(0);
result
}
}
#[derive(Default, Debug)]
pub struct GlobalScope {
pub global: HashMap<String, Value>,
global: HashMap<String, Value>,
}
// TODO create a print function for scope that print recursively all the scopes
// impl Display for GlobalScope {
// fn fmt(&self, f: &mut Formatter) -> Result {
// writeln!(f, "global: ")?;
// let global_vars: Vec<String> = self
// .global
// .iter()
// .map(|(key, val)| format!("var: {0} => val: {1}", key, val))
// .collect();
// writeln!(f, "{}", global_vars.join("\n"))?;
// Ok(())
// }
// }
impl Environment for GlobalScope {
fn create(&mut self, name: &str, node: Value) -> bool {
match self.global.contains_key(name) {
@ -50,28 +41,48 @@ impl Environment for GlobalScope {
fn get(&self, name: &str) -> Option<Value> {
self.global.get(name).cloned()
}
fn recursive_to_string(&self, level: usize) -> (String, usize) {
let mut result = String::new();
result.push_str("global:\n");
for (key, val) in self.global.iter() {
result.push_str(&format!(" var: {} => val: {}\n", key, val));
}
(result, level)
}
}
pub struct FunctionScope<'a> {
pub parent: &'a mut dyn Environment,
pub vars: HashMap<String, Value>,
pub struct LocalScope<'a> {
name: String,
parent: &'a mut dyn Environment,
vars: HashMap<String, Value>,
}
impl FunctionScope<'_> {
pub fn new<'a>(
env: &'a mut (dyn Environment + 'a),
impl LocalScope<'_> {
pub fn new_function<'a>(
parent: &'a mut (dyn Environment + 'a),
name: &str,
arg_names: &Rc<Vec<String>>,
args: &[Value],
) -> FunctionScope<'a> {
) -> 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());
}
FunctionScope { parent: env, vars }
let name = format!("{}({})", name, arg_names.join(", "));
LocalScope { name, parent, vars }
}
pub fn new_block<'a>(parent: &'a mut (dyn Environment + 'a)) -> LocalScope<'a> {
LocalScope {
name: "block".to_string(),
parent,
vars: HashMap::new(),
}
}
}
impl<'a> Environment for FunctionScope<'a> {
impl<'a> Environment for LocalScope<'a> {
fn create(&mut self, name: &str, node: Value) -> bool {
match self.vars.contains_key(name) {
true => false,
@ -95,4 +106,14 @@ impl<'a> Environment for FunctionScope<'a> {
None => self.parent.get(name),
}
}
fn recursive_to_string(&self, level: usize) -> (String, usize) {
let (mut result, level_deep) = self.parent.recursive_to_string(level + 1);
let indentation = " ".repeat(level_deep - level);
result.push_str(&format!("{}{}:\n", indentation, self.name));
for (key, val) in self.vars.iter() {
result.push_str(&format!("{} var: {} => val: {}\n", indentation, key, val));
}
(result, level_deep)
}
}

View File

@ -1,6 +1,6 @@
use super::{
compute::{BinaryCompute, UnaryCompute, ValueCompute},
environment::{Environment, FunctionScope},
environment::{Environment, LocalScope},
machine::{ControlFlow, Execution},
};
use crate::parser::syntax::{Expr, Value};
@ -53,7 +53,8 @@ impl Evaluate for Expr {
args.len()
));
}
let mut fn_env = FunctionScope::new(env, arg_names, &args);
let mut fn_env = LocalScope::new_function(env, name, arg_names, &args);
// println!("{}", fn_env.to_string());
if let ControlFlow::Return(value) = body.execute(&mut fn_env)? {
Ok(value)
} else {

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use super::{
compute::ValueCompute,
environment::{Environment, GlobalScope},
environment::{Environment, GlobalScope, LocalScope},
evaluate::Evaluate,
};
use crate::parser::syntax::{Stat, Value};
@ -21,9 +21,9 @@ pub trait Execution {
impl Execution for Vec<Stat> {
fn execute(&self, env: &mut dyn Environment) -> Result<ControlFlow, String> {
// TODO test create a new scope for each block / with tests
let mut env = LocalScope::new_block(env);
for stat in self {
match stat.execute(env)? {
match stat.execute(&mut env)? {
ControlFlow::Next => (),
flow @ (ControlFlow::Break | ControlFlow::Continue) => return Ok(flow),
ControlFlow::Return(value) => return Ok(ControlFlow::Return(value)),
@ -76,13 +76,17 @@ impl Execution for Stat {
let value = expr.evaluate(env)?;
env.assign(name, value);
}
// println!("{}", env.to_string());
Ok(ControlFlow::Next)
}
Stat::Break => Ok(ControlFlow::Break),
Stat::Continue => Ok(ControlFlow::Continue),
Stat::FunDefine(ref name, ref args, ref body) => {
match env.create(name, Value::Function(args.clone(), body.clone())) {
true => Ok(ControlFlow::Next),
true => {
// println!("{}", env.to_string());
Ok(ControlFlow::Next)
}
false => Err(format!("Function {} already defined", name)),
}
}
@ -93,7 +97,10 @@ impl Execution for Stat {
Value::None
};
match env.create(name, value) {
true => Ok(ControlFlow::Next),
true => {
// println!("{}", env.to_string());
Ok(ControlFlow::Next)
}
false => Err(format!("Variable {} already defined", name)),
}
}

View File

@ -1015,4 +1015,28 @@ mod tests {
};
assert_eq!(e, b'\'');
}
#[test]
fn test_local_block_env() {
let environment = run_file("test_programs/local_block_env.duck").unwrap();
let x = match environment.get("x") {
Some(Value::Number(value)) => value,
_ => panic!("x should be Value::Number"),
};
assert_eq!(x, 10);
let y = match environment.get("y") {
Some(Value::Number(value)) => value,
_ => panic!("y should be Value::Number"),
};
assert_eq!(y, 0);
let z = match environment.get("z") {
Some(Value::Number(value)) => value,
_ => panic!("z should be Value::Number"),
};
assert_eq!(z, 1);
assert!(match environment.get("n") {
Some(_) => false,
None => true,
});
}
}

View File

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

View File

@ -47,16 +47,6 @@ impl eframe::App for MyEguiApp {
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
}
}
@ -93,9 +83,10 @@ impl MyEguiApp {
let side_panel = egui::SidePanel::left("side_panel").show(ctx, |ui| {
ui.heading("Environment");
ui.separator();
for (name, value) in self.machine.env.global.iter() {
ui.label(format!("{}: {}", name, value));
}
// TODO fix for new features
// for (name, value) in self.machine.env.global.iter() {
// ui.label(format!("{}: {}", name, value));
// }
});
side_panel.response.rect
}

View File

@ -1,28 +1,32 @@
var x;
if (true) {
var x = true;
x = true;
} else {
var x = false;
x = false;
}
var y;
if (false) {
var y = true;
y = true;
} else {
var y = false;
y = false;
}
var z = false;
if (true) {
z = true;
}
var a;
if (false) {
var a = false;
a = false;
} else if (true) {
var a = true;
a = true;
}
var b;
if (false) {
var b = false;
b = false;
} else if (false) {
var b = false;
b = false;
} else if (false) {
var b = false;
b = false;
} else {
var b = true;
b = true;
}

View File

@ -0,0 +1,11 @@
var x = 0;
var y = 0;
var z = 0;
while (x < 10) {
var y = x;
if (x == 5) {
z = 1;
var n = 12;
}
x = x + 1;
}