feat(scope): local scope for blocks + better env print
parent
7ae18228a3
commit
76b364191a
32
README.md
32
README.md
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue