From 03b29b155dda35d395234259cb5facd0603d24c8 Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Fri, 24 May 2024 22:26:39 +0300 Subject: [PATCH 1/6] feat: added FruScope NativeObject feat: added import statement refactor: replaced todo.md with obsidian --- .gitignore | 2 +- Cargo.toml | 4 +-- obsidian/.gitignore | 1 + obsidian/FIXME.canvas | 13 +++++++ obsidian/TODO.canvas | 18 ++++++++++ obsidian/TOTHINK.canvas | 6 ++++ src/interpreter/mod.rs | 25 ++++++-------- src/interpreter/statement.rs | 48 +++++++++++++++++++++++++- src/interpreter/tree_sitter_parser.rs | 8 ++++- src/interpreter/value/native/object.rs | 12 ++++--- src/main.rs | 4 +-- src/stdlib/mod.rs | 1 + src/stdlib/scope/fru_scope.rs | 35 +++++++++++++++++++ src/stdlib/scope/mod.rs | 1 + tests/lib.rs | 2 +- todo.md | 33 ------------------ 16 files changed, 154 insertions(+), 59 deletions(-) create mode 100644 obsidian/.gitignore create mode 100644 obsidian/FIXME.canvas create mode 100644 obsidian/TODO.canvas create mode 100644 obsidian/TOTHINK.canvas create mode 100644 src/stdlib/mod.rs create mode 100644 src/stdlib/scope/fru_scope.rs create mode 100644 src/stdlib/scope/mod.rs delete mode 100644 todo.md diff --git a/.gitignore b/.gitignore index ed768f3..ca98cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target/ -Cargo.lock \ No newline at end of file +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index ab0dfb3..101275c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ serde_json = "1.0.115" snailquote = "0.3.1" thiserror = "1.0.58" tree-sitter = "0.22.5" -tree-sitter-frugurt = "0.0.10" -#tree-sitter-frugurt = { path = "../tree-sitter-frugurt" } +#tree-sitter-frugurt = "0.0.10" +tree-sitter-frugurt = { path = "../tree-sitter-frugurt" } #uid = "0.1.7" [dev-dependencies] diff --git a/obsidian/.gitignore b/obsidian/.gitignore new file mode 100644 index 0000000..0c5b2be --- /dev/null +++ b/obsidian/.gitignore @@ -0,0 +1 @@ +/.obsidian diff --git a/obsidian/FIXME.canvas b/obsidian/FIXME.canvas new file mode 100644 index 0000000..9af50a6 --- /dev/null +++ b/obsidian/FIXME.canvas @@ -0,0 +1,13 @@ +{ + "nodes":[ + {"id":"813b32f2728a4a94","x":-800,"y":-440,"width":640,"height":820,"type":"group","label":"ordinary"}, + {"id":"e6b12e213f195739","x":-1420,"y":-440,"width":520,"height":500,"type":"group","label":"critical"}, + {"id":"02fe9a5b84622932","x":-1400,"y":-414,"width":250,"height":79,"type":"text","text":"`fru_clone()` horror"}, + {"id":"cd9a0d2eaf046fee","type":"text","text":"implement Display for FruValue and fix for FruObject","x":-740,"y":-409,"width":250,"height":110}, + {"id":"2dc5ff956d956862","type":"text","text":"introduce a new error type for \"method/field not found\"","x":-740,"y":-275,"width":250,"height":130}, + {"id":"b18a8755b912d870","type":"text","text":"remove or fix BACKWARDS_MAP in Identifier (fails testing with some probability) !! probably already fixed","x":-740,"y":-100,"width":305,"height":170}, + {"id":"8b8b22b9de04c759","type":"text","text":"automate wasm module for book","x":-740,"y":120,"width":250,"height":60}, + {"id":"4cbdbc839acac840","type":"text","text":"make set expression, not statement","x":-740,"y":240,"width":250,"height":60} + ], + "edges":[] +} \ No newline at end of file diff --git a/obsidian/TODO.canvas b/obsidian/TODO.canvas new file mode 100644 index 0000000..5473ecf --- /dev/null +++ b/obsidian/TODO.canvas @@ -0,0 +1,18 @@ +{ + "nodes":[ + {"id":"feb6594a9d261a54","type":"text","text":"implement modules and imports","x":340,"y":-20,"width":250,"height":60}, + {"id":"4ab66adbfddd4d15","type":"text","text":"implement scope manipulation features","x":-40,"y":-100,"width":250,"height":60}, + {"id":"41499d9f6eafdeec","type":"text","text":"add trait polymorphism for native types","x":-480,"y":-50,"width":250,"height":60,"color":"4"}, + {"id":"1c18b7117da43e6e","type":"text","text":"implement \"evil\" features","x":-460,"y":-167,"width":250,"height":67,"color":"5"}, + {"id":"18df0d00841f02bd","type":"text","text":"add collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-60,"y":120,"width":250,"height":182,"color":"3"}, + {"id":"550b9c0e5c7cd03a","type":"text","text":"add derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":680,"y":249,"width":250,"height":160}, + {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":680,"y":140,"width":250,"height":60}, + {"id":"5d4e5bd937436926","type":"text","text":"add computed properties","x":-700,"y":140,"width":250,"height":60,"color":"4"} + ], + "edges":[ + {"id":"4f43eea514ca8881","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, + {"id":"94361d23f182cbe8","fromNode":"4ab66adbfddd4d15","fromSide":"right","toNode":"feb6594a9d261a54","toSide":"left"}, + {"id":"e3e0be30be175453","fromNode":"1c18b7117da43e6e","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, + {"id":"8706d0e0442108f4","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"18df0d00841f02bd","toSide":"left"} + ] +} \ No newline at end of file diff --git a/obsidian/TOTHINK.canvas b/obsidian/TOTHINK.canvas new file mode 100644 index 0000000..23bd399 --- /dev/null +++ b/obsidian/TOTHINK.canvas @@ -0,0 +1,6 @@ +{ + "nodes":[ + {"id":"901340d58aa31311","x":-620,"y":-360,"width":250,"height":130,"type":"text","text":"introduce new wrapper extensions like `wrap_ok`, `wrap_err`, `wrap_value`"} + ], + "edges":[] +} \ No newline at end of file diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b47ad0a..29af3b8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,14 +1,11 @@ -mod builtins; -mod control; -mod error; -mod expression; -mod helpers; -mod identifier; -mod runner; -mod scope; -mod statement; -mod tree_sitter_parser; -mod value; - -pub use identifier::reset_poison; -pub use runner::execute_file; +pub mod builtins; +pub mod control; +pub mod error; +pub mod expression; +pub mod helpers; +pub mod identifier; +pub mod runner; +pub mod scope; +pub mod statement; +pub mod tree_sitter_parser; +pub mod value; diff --git a/src/interpreter/statement.rs b/src/interpreter/statement.rs index c1b97dd..5802303 100644 --- a/src/interpreter/statement.rs +++ b/src/interpreter/statement.rs @@ -1,20 +1,26 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc}; use crate::interpreter::{ control::Control, + error::FruError, expression::FruExpression, identifier::{Identifier, OperatorIdentifier}, + runner, scope::Scope, value::fru_type::{FruField, FruType, Property, TypeType}, value::fru_value::FruValue, value::function::{FormalParameters, FruFunction}, value::operator::AnyOperator, }; +use crate::stdlib::scope::fru_scope::FruScope; pub type RawMethods = Vec<(bool, Identifier, FormalParameters, Rc)>; #[derive(Debug, Clone)] pub enum FruStatement { + SourceCode { + body: Vec, + }, Block { body: Vec, }, @@ -66,11 +72,20 @@ pub enum FruStatement { static_properties: HashMap, methods: RawMethods, }, + Import { + path: Box, + }, } impl FruStatement { pub fn execute(&self, scope: Rc) -> Result<(), Control> { match self { + FruStatement::SourceCode { body } => { + for statement in body { + statement.execute(scope.clone())?; + } + } + FruStatement::Block { body } => { let new_scope = Scope::new_with_parent(scope.clone()); @@ -243,6 +258,37 @@ impl FruStatement { ), )?; } + + FruStatement::Import { path } => { + let path = path.evaluate(scope.clone())?; + + let path = match path { + FruValue::String(path) => path, + + _ => { + return Control::new_err(format!( + "Expected `String` in import path, got `{}`", + path.get_type_identifier() + )); + } + }; + + let path = PathBuf::from(path); + + let name = path + .file_stem() + .ok_or_else(|| FruError::new(format!("File path \"{:?}\" is not valid", path)))? + .to_str() + .unwrap(); + + let result_scope = match runner::execute_file(&path) { + Ok(x) => x, + + Err(err) => return Err(Control::Error(err)), + }; + + scope.let_variable(Identifier::new(name), FruScope::new_value(result_scope))?; + } } Ok(()) diff --git a/src/interpreter/tree_sitter_parser.rs b/src/interpreter/tree_sitter_parser.rs index 410a9c5..bad405e 100644 --- a/src/interpreter/tree_sitter_parser.rs +++ b/src/interpreter/tree_sitter_parser.rs @@ -217,7 +217,7 @@ fn search_for_errors(ast: Node) -> ParseError { fn parse_statement(ast: NodeWrapper) -> Result { let result_statement = match ast.grammar_name() { "source_file" => { - FruStatement::Block { + FruStatement::SourceCode { body: ast.parse_children("body", parse_statement)?, } } @@ -379,6 +379,12 @@ fn parse_statement(ast: NodeWrapper) -> Result { } } + "import_statement" => { + FruStatement::Import { + path: ast.parse_child_expression("path")?.wrap_box(), + } + } + unexpected => { return Err(ParseError::InvalidAst { position: ast.range(), diff --git a/src/interpreter/value/native/object.rs b/src/interpreter/value/native/object.rs index 451ee30..2a5d5b6 100644 --- a/src/interpreter/value/native/object.rs +++ b/src/interpreter/value/native/object.rs @@ -44,17 +44,21 @@ pub trait INativeObject { )) } - fn fru_clone(&self) -> FruValue { - panic!(); + fn fru_clone(&self, value: &NativeObject) -> NativeObject { + value.clone() } } #[derive(Clone)] pub struct NativeObject { - pub internal: Rc, + internal: Rc, } impl NativeObject { + pub fn new(internal: Rc) -> Self { + Self { internal } + } + pub fn get_type_identifier(&self) -> Identifier { self.internal.get_type_identifier() } @@ -80,6 +84,6 @@ impl NativeObject { } pub fn fru_clone(&self) -> FruValue { - self.internal.fru_clone() + FruValue::NativeObject(self.internal.fru_clone(self)) } } diff --git a/src/main.rs b/src/main.rs index 031c647..c3fc662 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,11 @@ use std::{path::PathBuf, time::Instant}; +use crate::interpreter::runner::execute_file; use clap::Parser; -use crate::interpreter::execute_file; - mod interpreter; +mod stdlib; #[derive(Parser, Debug)] struct Args { diff --git a/src/stdlib/mod.rs b/src/stdlib/mod.rs new file mode 100644 index 0000000..88e6afc --- /dev/null +++ b/src/stdlib/mod.rs @@ -0,0 +1 @@ +pub mod scope; diff --git a/src/stdlib/scope/fru_scope.rs b/src/stdlib/scope/fru_scope.rs new file mode 100644 index 0000000..d57deab --- /dev/null +++ b/src/stdlib/scope/fru_scope.rs @@ -0,0 +1,35 @@ +use std::rc::Rc; + +use crate::interpreter::{ + error::FruError, + identifier::Identifier, + scope::Scope, + value::fru_value::FruValue, + value::native::object::{INativeObject, NativeObject}, +}; + +pub struct FruScope { + scope: Rc, +} + +impl FruScope { + pub fn new_value(scope: Rc) -> FruValue { + FruValue::NativeObject(NativeObject::new(Rc::new(Self { scope }))) + } +} + +impl INativeObject for FruScope { + fn get_type_identifier(&self) -> Identifier { + Identifier::new("Scope") + } + + fn get_prop(&self, ident: Identifier) -> Result { + self.scope.get_variable(ident) + } + + fn set_prop(&self, ident: Identifier, value: FruValue) -> Result<(), FruError> { + self.scope + .set_variable(ident, value.clone()) + .or_else(|_| self.scope.let_variable(ident, value)) + } +} diff --git a/src/stdlib/scope/mod.rs b/src/stdlib/scope/mod.rs new file mode 100644 index 0000000..7ac7f6f --- /dev/null +++ b/src/stdlib/scope/mod.rs @@ -0,0 +1 @@ +pub mod fru_scope; diff --git a/tests/lib.rs b/tests/lib.rs index cf01904..f33b611 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -4,7 +4,7 @@ use std::io::Write; use tempfile::NamedTempFile; -use crate::interpreter::{execute_file, reset_poison}; +use crate::interpreter::{identifier::reset_poison, runner::execute_file}; #[path = "../src/interpreter/mod.rs"] mod interpreter; diff --git a/todo.md b/todo.md deleted file mode 100644 index de7284d..0000000 --- a/todo.md +++ /dev/null @@ -1,33 +0,0 @@ -# Possible bugs - -- `fru_clone()` horror - -# TODO - -- add `overload` keyword for functions and methods -- add variadic parameters -- add trait polymorphism for native types - - move FruValue::Object and FruValue::Type to FruValue:NativeObject - - add collections - - list - - set - - map - - tuple? -- implement scope manipulation features - - implement modules and imports -- implement "evil" features -- make cd for windows and linux releases -- add derivation and implicit derivation, and make them overridable (the main reason is equality of objects) -- add computed properties - -# Needed fixes - -- implement Display for FruValue and fix for FruObject -- remove or fix BACKWARDS_MAP in Identifier (fails testing with some probability) !! probably already fixed -- introduce a new error type for "method/field not found" -- automate wasm module for book -- make set expression, not statement - -# Possible improvements - -- introduce new wrapper extensions like `wrap_ok`, `wrap_err`, `wrap_value`? Needs lots of thinking. From a908ececfc4abc19e13574e42259d18e25a1346e Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Thu, 13 Jun 2024 14:15:35 +0300 Subject: [PATCH 2/6] refactor: switch to stable toolchain feat: implement scope manipulation features refactor: make import an expression test: add tests for scope manipulation --- obsidian/FIXME.canvas | 13 +- obsidian/TODO.canvas | 27 +- rust-toolchain.toml | 2 - src/interpreter/builtins/operators.rs | 67 +-- src/interpreter/expression.rs | 94 ++-- src/interpreter/identifier.rs | 7 +- src/interpreter/runner.rs | 5 +- src/interpreter/scope.rs | 30 +- src/interpreter/statement.rs | 62 ++- src/interpreter/tree_sitter_parser.rs | 405 ++++++++---------- src/interpreter/value/fru_object.rs | 10 +- src/interpreter/value/fru_value.rs | 44 +- src/interpreter/value/native/object.rs | 11 +- src/main.rs | 2 - src/stdlib/scope/fru_scope.rs | 19 +- tests/expression/block_expression_tests.rs | 2 +- .../expression/curry_call_expression_tests.rs | 8 +- tests/expression/function_expression_tests.rs | 22 +- .../instantiation_expression_tests.rs | 4 +- tests/expression/variable_expression_tests.rs | 2 +- tests/lib.rs | 6 +- .../literal_expression/literal_bool_tests.rs | 2 +- tests/oop/class_tests.rs | 10 +- tests/oop/data_tests.rs | 4 +- tests/oop/general_tests.rs | 16 +- tests/oop/property_tests.rs | 90 ++-- tests/oop/static_tests.rs | 20 +- tests/oop/struct_tests.rs | 10 +- tests/scope_manipulation/basics.rs | 79 ++++ tests/scope_manipulation/mod.rs | 1 + 30 files changed, 593 insertions(+), 481 deletions(-) delete mode 100644 rust-toolchain.toml create mode 100644 tests/scope_manipulation/basics.rs create mode 100644 tests/scope_manipulation/mod.rs diff --git a/obsidian/FIXME.canvas b/obsidian/FIXME.canvas index 9af50a6..5a85a2e 100644 --- a/obsidian/FIXME.canvas +++ b/obsidian/FIXME.canvas @@ -1,13 +1,12 @@ { "nodes":[ - {"id":"813b32f2728a4a94","x":-800,"y":-440,"width":640,"height":820,"type":"group","label":"ordinary"}, - {"id":"e6b12e213f195739","x":-1420,"y":-440,"width":520,"height":500,"type":"group","label":"critical"}, - {"id":"02fe9a5b84622932","x":-1400,"y":-414,"width":250,"height":79,"type":"text","text":"`fru_clone()` horror"}, + {"id":"813b32f2728a4a94","type":"group","x":-800,"y":-440,"width":640,"height":820,"label":"ordinary"}, + {"id":"e6b12e213f195739","type":"group","x":-1420,"y":-440,"width":520,"height":500,"label":"critical"}, + {"id":"02fe9a5b84622932","type":"text","text":"`fru_clone()` horror","x":-1400,"y":-414,"width":250,"height":79}, {"id":"cd9a0d2eaf046fee","type":"text","text":"implement Display for FruValue and fix for FruObject","x":-740,"y":-409,"width":250,"height":110}, - {"id":"2dc5ff956d956862","type":"text","text":"introduce a new error type for \"method/field not found\"","x":-740,"y":-275,"width":250,"height":130}, - {"id":"b18a8755b912d870","type":"text","text":"remove or fix BACKWARDS_MAP in Identifier (fails testing with some probability) !! probably already fixed","x":-740,"y":-100,"width":305,"height":170}, - {"id":"8b8b22b9de04c759","type":"text","text":"automate wasm module for book","x":-740,"y":120,"width":250,"height":60}, - {"id":"4cbdbc839acac840","type":"text","text":"make set expression, not statement","x":-740,"y":240,"width":250,"height":60} + {"id":"b18a8755b912d870","type":"text","text":"remove or fix BACKWARDS_MAP in Identifier (fails testing with some probability) !! probably already fixed","x":-740,"y":-260,"width":305,"height":170}, + {"id":"8b8b22b9de04c759","type":"text","text":"automate wasm module for book","x":-740,"y":-60,"width":250,"height":60}, + {"id":"4cbdbc839acac840","type":"text","text":"make set expression, not statement","x":-740,"y":40,"width":250,"height":60} ], "edges":[] } \ No newline at end of file diff --git a/obsidian/TODO.canvas b/obsidian/TODO.canvas index 5473ecf..2e12458 100644 --- a/obsidian/TODO.canvas +++ b/obsidian/TODO.canvas @@ -1,18 +1,27 @@ { "nodes":[ - {"id":"feb6594a9d261a54","type":"text","text":"implement modules and imports","x":340,"y":-20,"width":250,"height":60}, - {"id":"4ab66adbfddd4d15","type":"text","text":"implement scope manipulation features","x":-40,"y":-100,"width":250,"height":60}, - {"id":"41499d9f6eafdeec","type":"text","text":"add trait polymorphism for native types","x":-480,"y":-50,"width":250,"height":60,"color":"4"}, - {"id":"1c18b7117da43e6e","type":"text","text":"implement \"evil\" features","x":-460,"y":-167,"width":250,"height":67,"color":"5"}, - {"id":"18df0d00841f02bd","type":"text","text":"add collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-60,"y":120,"width":250,"height":182,"color":"3"}, - {"id":"550b9c0e5c7cd03a","type":"text","text":"add derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":680,"y":249,"width":250,"height":160}, - {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":680,"y":140,"width":250,"height":60}, - {"id":"5d4e5bd937436926","type":"text","text":"add computed properties","x":-700,"y":140,"width":250,"height":60,"color":"4"} + {"id":"0c274527d795ce38","x":-80,"y":600,"width":400,"height":400,"type":"group","label":"OPTIMIZATIONS"}, + {"id":"0e8289b07d6ca556","type":"group","x":-600,"y":600,"width":400,"height":380,"label":"CI/CD"}, + {"id":"b9c4b54397d2bf2d","x":-43,"y":666,"width":250,"height":87,"type":"text","text":"macro for computing hash of ident in compile time"}, + {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":-580,"y":640,"width":250,"height":50}, + {"id":"070a7b543247f7a2","type":"text","text":"simplified syntax for read-only properties","x":-290,"y":360,"width":250,"height":60}, + {"id":"5d4e5bd937436926","type":"text","text":"computed properties","x":-640,"y":360,"width":250,"height":60,"color":"4"}, + {"id":"18df0d00841f02bd","type":"text","text":"collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-80,"y":10,"width":250,"height":182,"color":"3"}, + {"id":"97a68fbd25b52ede","x":260,"y":-280,"width":250,"height":60,"type":"text","text":"destructuring let statements"}, + {"id":"1c18b7117da43e6e","type":"text","text":"\"evil\" features","x":-460,"y":-180,"width":250,"height":67,"color":"5"}, + {"id":"41499d9f6eafdeec","type":"text","text":"trait polymorphism for native types","x":-460,"y":-40,"width":250,"height":60,"color":"4"}, + {"id":"4ab66adbfddd4d15","type":"text","text":"scope manipulation features","x":-80,"y":-180,"width":250,"height":60}, + {"id":"feb6594a9d261a54","type":"text","text":"modules and imports","x":260,"y":-180,"width":250,"height":60}, + {"id":"545169f683d1ebef","x":620,"y":-180,"width":250,"height":60,"type":"text","text":"import as expression"}, + {"id":"550b9c0e5c7cd03a","type":"text","text":"derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":620,"y":300,"width":250,"height":160} ], "edges":[ {"id":"4f43eea514ca8881","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, {"id":"94361d23f182cbe8","fromNode":"4ab66adbfddd4d15","fromSide":"right","toNode":"feb6594a9d261a54","toSide":"left"}, {"id":"e3e0be30be175453","fromNode":"1c18b7117da43e6e","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, - {"id":"8706d0e0442108f4","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"18df0d00841f02bd","toSide":"left"} + {"id":"8706d0e0442108f4","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"18df0d00841f02bd","toSide":"left"}, + {"id":"10698732105d19f9","fromNode":"5d4e5bd937436926","fromSide":"right","toNode":"070a7b543247f7a2","toSide":"left"}, + {"id":"889e945801477a2c","fromNode":"97a68fbd25b52ede","fromSide":"right","toNode":"545169f683d1ebef","toSide":"left"}, + {"id":"3f046cb411b32fae","fromNode":"feb6594a9d261a54","fromSide":"right","toNode":"545169f683d1ebef","toSide":"left"} ] } \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 271800c..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" \ No newline at end of file diff --git a/src/interpreter/builtins/operators.rs b/src/interpreter/builtins/operators.rs index 0cad6cc..69a1439 100644 --- a/src/interpreter/builtins/operators.rs +++ b/src/interpreter/builtins/operators.rs @@ -33,35 +33,44 @@ macro_rules! operator_group { } pub fn builtin_operators() -> HashMap { - let mut res = HashMap::from(operator_group!(Id::for_number(), Id::for_number(), [ - (for_plus, num_plus_num), - (for_minus, num_minus_num), - (for_multiply, num_mul_num), - (for_divide, num_div_num), - (for_mod, num_mod_num), - (for_pow, num_pow_num), - (for_less, num_less_num), - (for_less_eq, num_less_eq_num), - (for_greater, num_greater_num), - (for_greater_eq, num_greater_eq_num), - (for_eq, num_eq_num), - (for_not_eq, num_not_eq_num) - ])); - - res.extend(operator_group!(Id::for_bool(), Id::for_bool(), [ - (for_and, bool_and_bool), - (for_or, bool_or_bool) - ])); - - res.extend(operator_group!(Id::for_string(), Id::for_string(), [ - (for_combine, string_concat), - (for_less, string_less_string), - (for_less_eq, string_less_eq_string), - (for_greater, string_greater_string), - (for_greater_eq, string_greater_eq_string), - (for_eq, string_eq_string), - (for_not_eq, string_not_eq_string) - ])); + let mut res = HashMap::from(operator_group!( + Id::for_number(), + Id::for_number(), + [ + (for_plus, num_plus_num), + (for_minus, num_minus_num), + (for_multiply, num_mul_num), + (for_divide, num_div_num), + (for_mod, num_mod_num), + (for_pow, num_pow_num), + (for_less, num_less_num), + (for_less_eq, num_less_eq_num), + (for_greater, num_greater_num), + (for_greater_eq, num_greater_eq_num), + (for_eq, num_eq_num), + (for_not_eq, num_not_eq_num) + ] + )); + + res.extend(operator_group!( + Id::for_bool(), + Id::for_bool(), + [(for_and, bool_and_bool), (for_or, bool_or_bool)] + )); + + res.extend(operator_group!( + Id::for_string(), + Id::for_string(), + [ + (for_combine, string_concat), + (for_less, string_less_string), + (for_less_eq, string_less_eq_string), + (for_greater, string_greater_string), + (for_greater_eq, string_greater_eq_string), + (for_eq, string_eq_string), + (for_not_eq, string_not_eq_string) + ] + )); res.extend([ ( diff --git a/src/interpreter/expression.rs b/src/interpreter/expression.rs index caa2af9..a783c8c 100644 --- a/src/interpreter/expression.rs +++ b/src/interpreter/expression.rs @@ -1,13 +1,15 @@ -use std::rc::Rc; +use std::{path::PathBuf, rc::Rc}; use crate::interpreter::{ control::Control, identifier::{Identifier, OperatorIdentifier}, + runner, scope::Scope, statement::FruStatement, value::fru_value::FruValue, value::function::{ArgumentList, EvaluatedArgumentList, FormalParameters, FruFunction}, }; +use crate::stdlib::scope::fru_scope::{extract_scope_from_value, FruScope}; #[derive(Debug, Clone)] pub enum FruExpression { @@ -17,6 +19,7 @@ pub enum FruExpression { Variable { ident: Identifier, }, + ScopeAccessor, Function { args: FormalParameters, body: Rc, @@ -25,6 +28,11 @@ pub enum FruExpression { body: Vec, expr: Box, }, + ScopeModifier { + what: Box, + body: Vec, + expr: Box, + }, Call { what: Box, args: ArgumentList, @@ -51,6 +59,9 @@ pub enum FruExpression { then_body: Box, else_body: Box, }, + Import { + path: Box, + }, } fn eval_args(args: &ArgumentList, scope: Rc) -> Result { @@ -61,7 +72,7 @@ fn eval_args(args: &ArgumentList, scope: Rc) -> Result Result<_, Control> { Ok((*ident, arg.evaluate(scope.clone())?)) }) - .try_collect()?, + .collect::>()?, }) } @@ -72,19 +83,17 @@ impl FruExpression { FruExpression::Variable { ident } => Ok(scope.get_variable(*ident)?), - FruExpression::Function { args, body } => { - Ok(FruFunction { - argument_idents: args.clone(), - body: body.clone(), - scope: scope.clone(), - } - .into()) + FruExpression::ScopeAccessor => Ok(FruScope::new_value(scope)), + + FruExpression::Function { args, body } => Ok(FruFunction { + argument_idents: args.clone(), + body: body.clone(), + scope: scope.clone(), } + .into()), FruExpression::Block { body, expr } => { - if !body.is_empty() { - scope = Scope::new_with_parent(scope.clone()) - }; + scope = Scope::new_with_parent(scope.clone()); for statement in body { statement.execute(scope.clone())?; @@ -92,6 +101,24 @@ impl FruExpression { expr.evaluate(scope) } + FruExpression::ScopeModifier { what, body, expr } => { + let what = what.evaluate(scope)?; + let new_scope = match extract_scope_from_value(&what) { + Some(x) => x, + None => { + return Control::new_err(format!( + "Expected `Scope` in scope modifier expression, got `{}`", + what.get_type_identifier() + )) + } + }; + + for statement in body { + statement.execute(new_scope.clone())?; + } + + expr.evaluate(new_scope) + } FruExpression::Call { what, args } => { let callee = what.evaluate(scope.clone())?; @@ -143,23 +170,40 @@ impl FruExpression { condition, then_body, else_body, - } => { - match condition.evaluate(scope.clone())? { - FruValue::Bool(b) => { - if b { - then_body.evaluate(scope.clone()) - } else { - else_body.evaluate(scope.clone()) - } + } => match condition.evaluate(scope.clone())? { + FruValue::Bool(b) => { + if b { + then_body.evaluate(scope.clone()) + } else { + else_body.evaluate(scope.clone()) } + } + + unexpected => Control::new_err(format!( + "Expected `Bool` in if condition, got `{}`", + unexpected.get_type_identifier() + )), + }, + + FruExpression::Import { path } => { + let path = path.evaluate(scope.clone())?; - unexpected => { - Control::new_err(format!( - "Expected `Bool` in if condition, got `{}`", - unexpected.get_type_identifier() + let path = match path { + FruValue::String(path) => path, + + _ => { + return Control::new_err(format!( + "Expected `String` in import path, got `{}`", + path.get_type_identifier() )) } - } + }; + + let path = PathBuf::from(path); + + let result_scope = runner::execute_file(&path)?; + + Ok(FruScope::new_value(result_scope)) } } } diff --git a/src/interpreter/identifier.rs b/src/interpreter/identifier.rs index 514342f..a6f225c 100644 --- a/src/interpreter/identifier.rs +++ b/src/interpreter/identifier.rs @@ -1,10 +1,5 @@ use std::{ - collections::HashMap, - fmt::Debug, - fmt::Display, - hash::DefaultHasher, - hash::Hash, - hash::Hasher, + collections::HashMap, fmt::Debug, fmt::Display, hash::DefaultHasher, hash::Hash, hash::Hasher, sync::Mutex, }; diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index cc682b0..520ae1f 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -1,9 +1,10 @@ -use std::{path::Path, rc::Rc}; +use std::{fs::read_to_string, path::Path, rc::Rc}; use crate::interpreter::{control::Control, error::FruError, scope::Scope, tree_sitter_parser}; pub fn execute_file(path: &Path) -> Result, FruError> { - let source_code = std::fs::read_to_string(path).unwrap(); + let source_code = read_to_string(path) + .map_err(|err| FruError::new(format!("Error reading file {path:?} {err}")))?; let ast = match tree_sitter_parser::parse(source_code) { Ok(ast) => ast, diff --git a/src/interpreter/scope.rs b/src/interpreter/scope.rs index 6c75a8f..6354707 100644 --- a/src/interpreter/scope.rs +++ b/src/interpreter/scope.rs @@ -98,12 +98,10 @@ impl Scope { Ok(op.clone()) } else { match &self.parent { - ScopeAncestor::None => { - Err(FruError::new(format!( - "operator `{:?}` does not exist", - ident - ))) - } + ScopeAncestor::None => Err(FruError::new(format!( + "operator `{:?}` does not exist", + ident + ))), ScopeAncestor::Parent(parent) | ScopeAncestor::Object { parent, .. } | ScopeAncestor::Type { parent, .. } => parent.get_operator(ident), @@ -118,6 +116,10 @@ impl Scope { pub fn has_variable(&self, ident: Identifier) -> bool { self.variables.borrow().contains_key(&ident) } + + pub fn let_set_variable(&self, ident: Identifier, value: FruValue) { + self.variables.borrow_mut().insert(ident, value); + } } impl ScopeAncestor { @@ -144,17 +146,13 @@ impl ScopeAncestor { ScopeAncestor::Parent(parent) => parent.set_variable(ident, value), - ScopeAncestor::Object { object, parent } => { - object - .set_prop(ident, value.clone()) - .or_else(|_| parent.set_variable(ident, value)) - } + ScopeAncestor::Object { object, parent } => object + .set_prop(ident, value.clone()) + .or_else(|_| parent.set_variable(ident, value)), - ScopeAncestor::Type { type_, parent } => { - type_ - .set_prop(ident, value.clone()) - .or_else(|_| parent.set_variable(ident, value)) - } + ScopeAncestor::Type { type_, parent } => type_ + .set_prop(ident, value.clone()) + .or_else(|_| parent.set_variable(ident, value)), } } } diff --git a/src/interpreter/statement.rs b/src/interpreter/statement.rs index 5802303..01e3b48 100644 --- a/src/interpreter/statement.rs +++ b/src/interpreter/statement.rs @@ -1,19 +1,18 @@ -use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use crate::interpreter::{ control::Control, - error::FruError, expression::FruExpression, identifier::{Identifier, OperatorIdentifier}, - runner, scope::Scope, value::fru_type::{FruField, FruType, Property, TypeType}, value::fru_value::FruValue, value::function::{FormalParameters, FruFunction}, value::operator::AnyOperator, }; -use crate::stdlib::scope::fru_scope::FruScope; +use crate::stdlib::scope::fru_scope::extract_scope_from_value; +// TODO: make normal struct pub type RawMethods = Vec<(bool, Identifier, FormalParameters, Rc)>; #[derive(Debug, Clone)] @@ -24,6 +23,10 @@ pub enum FruStatement { Block { body: Vec, }, + ScopeModifier { + what: Box, + body: Vec, + }, Expression { value: Box, }, @@ -72,9 +75,6 @@ pub enum FruStatement { static_properties: HashMap, methods: RawMethods, }, - Import { - path: Box, - }, } impl FruStatement { @@ -94,6 +94,23 @@ impl FruStatement { } } + FruStatement::ScopeModifier { what, body } => { + let what = what.evaluate(scope)?; + let new_scope = match extract_scope_from_value(&what) { + Some(x) => x, + None => { + return Control::new_err(format!( + "Expected `Scope` in scope modifier statement, got `{}`", + what.get_type_identifier() + )) + } + }; + + for statement in body { + statement.execute(new_scope.clone())?; + } + } + FruStatement::Expression { value } => { value.evaluate(scope.clone())?; } @@ -258,37 +275,6 @@ impl FruStatement { ), )?; } - - FruStatement::Import { path } => { - let path = path.evaluate(scope.clone())?; - - let path = match path { - FruValue::String(path) => path, - - _ => { - return Control::new_err(format!( - "Expected `String` in import path, got `{}`", - path.get_type_identifier() - )); - } - }; - - let path = PathBuf::from(path); - - let name = path - .file_stem() - .ok_or_else(|| FruError::new(format!("File path \"{:?}\" is not valid", path)))? - .to_str() - .unwrap(); - - let result_scope = match runner::execute_file(&path) { - Ok(x) => x, - - Err(err) => return Err(Control::Error(err)), - }; - - scope.let_variable(Identifier::new(name), FruScope::new_value(result_scope))?; - } } Ok(()) diff --git a/src/interpreter/tree_sitter_parser.rs b/src/interpreter/tree_sitter_parser.rs index bad405e..7debee4 100644 --- a/src/interpreter/tree_sitter_parser.rs +++ b/src/interpreter/tree_sitter_parser.rs @@ -103,11 +103,9 @@ impl<'a> NodeWrapper<'a> { } fn text(&self) -> Result<&'a str, ParseError> { - self.node.utf8_text(self.source).map_err(|x| { - ParseError::Utf8Error { - position: self.node.range(), - error: x, - } + self.node.utf8_text(self.source).map_err(|x| ParseError::Utf8Error { + position: self.node.range(), + error: x, }) } @@ -119,12 +117,10 @@ impl<'a> NodeWrapper<'a> { match self.node.child_by_field_name(name) { Some(x) => Ok(Self::new(x, self.source)), - None => { - Err(ParseError::MissingAst { - position: self.node.range(), - name: name.to_string(), - }) - } + None => Err(ParseError::MissingAst { + position: self.node.range(), + name: name.to_string(), + }), } } @@ -160,7 +156,7 @@ impl<'a> NodeWrapper<'a> { self.node .children_by_field_name(name, &mut self.node.walk()) .map(|x| parser(Self::new(x, self.source))) - .try_collect() + .collect() } fn parse_optional_child( @@ -216,66 +212,53 @@ fn search_for_errors(ast: Node) -> ParseError { fn parse_statement(ast: NodeWrapper) -> Result { let result_statement = match ast.grammar_name() { - "source_file" => { - FruStatement::SourceCode { - body: ast.parse_children("body", parse_statement)?, - } - } - - "block_statement" => { - FruStatement::Block { - body: ast.parse_children("body", parse_statement)?, - } - } - - "expression_statement" => { - FruStatement::Expression { - value: ast.parse_child_expression("value")?.wrap_box(), - } - } - - "let_statement" => { - FruStatement::Let { - ident: ast.get_child_ident("ident")?, - value: ast.parse_child_expression("value")?.wrap_box(), - } - } - - "set_statement" => { - FruStatement::Set { - ident: ast.get_child_ident("ident")?, - value: ast.parse_child_expression("value")?.wrap_box(), - } - } - - "set_prop_statement" => { - FruStatement::SetProp { - what: ast.parse_child_expression("what")?.wrap_box(), - ident: ast.get_child_ident("ident")?, - value: ast.parse_child_expression("value")?.wrap_box(), - } - } - - "if_statement" => { - FruStatement::If { - condition: ast.parse_child_expression("condition")?.wrap_box(), - then_body: ast.parse_child_statement("then_body")?.wrap_box(), - else_body: ast.parse_optional_child("else_body", parse_statement)?.map(Box::new), - } - } - - "while_statement" => { - FruStatement::While { - condition: ast.parse_child_expression("condition")?.wrap_box(), - body: ast.parse_child_statement("body")?.wrap_box(), - } - } - - "return_statement" => { - FruStatement::Return { - value: ast.parse_optional_child("value", parse_expression)?.map(Box::new), - } - } + "source_file" => FruStatement::SourceCode { + body: ast.parse_children("body", parse_statement)?, + }, + + "block_statement" => FruStatement::Block { + body: ast.parse_children("body", parse_statement)?, + }, + + "scope_modifier_statement" => FruStatement::ScopeModifier { + what: ast.parse_child_expression("what")?.wrap_box(), + body: ast.parse_children("body", parse_statement)?, + }, + + "expression_statement" => FruStatement::Expression { + value: ast.parse_child_expression("value")?.wrap_box(), + }, + + "let_statement" => FruStatement::Let { + ident: ast.get_child_ident("ident")?, + value: ast.parse_child_expression("value")?.wrap_box(), + }, + + "set_statement" => FruStatement::Set { + ident: ast.get_child_ident("ident")?, + value: ast.parse_child_expression("value")?.wrap_box(), + }, + + "set_prop_statement" => FruStatement::SetProp { + what: ast.parse_child_expression("what")?.wrap_box(), + ident: ast.get_child_ident("ident")?, + value: ast.parse_child_expression("value")?.wrap_box(), + }, + + "if_statement" => FruStatement::If { + condition: ast.parse_child_expression("condition")?.wrap_box(), + then_body: ast.parse_child_statement("then_body")?.wrap_box(), + else_body: ast.parse_optional_child("else_body", parse_statement)?.map(Box::new), + }, + + "while_statement" => FruStatement::While { + condition: ast.parse_child_expression("condition")?.wrap_box(), + body: ast.parse_child_statement("body")?.wrap_box(), + }, + + "return_statement" => FruStatement::Return { + value: ast.parse_optional_child("value", parse_expression)?.map(Box::new), + }, "break_statement" => FruStatement::Break, @@ -334,35 +317,31 @@ fn parse_statement(ast: NodeWrapper) -> Result { TypeMember::StaticField(f) => static_fields.push(f), - TypeMember::Property(p) => { - match properties.entry(p.ident) { - Entry::Occupied(_) => { - return Err(ParseError::Error { - position: ast.get_child("members")?.range(), - error: format!("Duplicate property: `{}`", p.ident), - }); - } - - Entry::Vacant(entry) => { - entry.insert(p); - } + TypeMember::Property(p) => match properties.entry(p.ident) { + Entry::Occupied(_) => { + return Err(ParseError::Error { + position: ast.get_child("members")?.range(), + error: format!("Duplicate property: `{}`", p.ident), + }); } - } - - TypeMember::StaticProperty(p) => { - match static_properties.entry(p.ident) { - Entry::Occupied(_) => { - return Err(ParseError::Error { - position: ast.get_child("members")?.range(), - error: format!("Duplicate static property: `{}`", p.ident), - }); - } - - Entry::Vacant(entry) => { - entry.insert(p); - } + + Entry::Vacant(entry) => { + entry.insert(p); + } + }, + + TypeMember::StaticProperty(p) => match static_properties.entry(p.ident) { + Entry::Occupied(_) => { + return Err(ParseError::Error { + position: ast.get_child("members")?.range(), + error: format!("Duplicate static property: `{}`", p.ident), + }); } - } + + Entry::Vacant(entry) => { + entry.insert(p); + } + }, } } @@ -379,12 +358,6 @@ fn parse_statement(ast: NodeWrapper) -> Result { } } - "import_statement" => { - FruStatement::Import { - path: ast.parse_child_expression("path")?.wrap_box(), - } - } - unexpected => { return Err(ParseError::InvalidAst { position: ast.range(), @@ -398,106 +371,90 @@ fn parse_statement(ast: NodeWrapper) -> Result { fn parse_expression(ast: NodeWrapper) -> Result { let result_expression = match ast.grammar_name() { - "nah_literal" => { - FruExpression::Literal { - value: FruValue::Nah, - } - } + "nah_literal" => FruExpression::Literal { + value: FruValue::Nah, + }, - "number_literal" => { - FruExpression::Literal { - value: FruValue::Number(ast.text()?.parse().unwrap()), - } - } + "number_literal" => FruExpression::Literal { + value: FruValue::Number(ast.text()?.parse().unwrap()), + }, - "bool_literal" => { - FruExpression::Literal { - value: FruValue::Bool(ast.text()?.parse().unwrap()), - } - } + "bool_literal" => FruExpression::Literal { + value: FruValue::Bool(ast.text()?.parse().unwrap()), + }, - "string_literal" => { - match unescape(&ast.text()?.replace("\\\n", "\n")) { - Ok(s) => { - FruExpression::Literal { - value: FruValue::String(s), - } - } + "string_literal" => match unescape(&ast.text()?.replace("\\\n", "\n")) { + Ok(s) => FruExpression::Literal { + value: FruValue::String(s), + }, - Err(err) => { - return Err(ParseError::InvalidAst { - position: ast.range(), - error: err.to_string(), - }); - } - } - } - - "variable" => { - FruExpression::Variable { - ident: ast.get_child_ident("ident")?, - } - } - - "function_expression" => { - FruExpression::Function { - args: ast.parse_child("parameters", parse_formal_parameters)?, - body: ast.parse_child("body", parse_function_body)?.wrap_rc(), - } - } - - "parenthesized_expression" => ast.parse_child_expression("expr")?, - - "block_expression" => { - FruExpression::Block { - body: ast.parse_children("body", parse_statement)?, - expr: ast.parse_child_expression("expr")?.wrap_box(), - } - } - - "call_expression" => { - FruExpression::Call { - what: ast.parse_child_expression("what")?.wrap_box(), - args: ast.parse_child("args", parse_argument_list)?, + Err(err) => { + return Err(ParseError::InvalidAst { + position: ast.range(), + error: err.to_string(), + }); } - } + }, - "curry_call_expression" => { - FruExpression::CurryCall { - what: ast.parse_child_expression("what")?.wrap_box(), - args: ast.parse_child("args", parse_argument_list)?, - } - } + "variable" => FruExpression::Variable { + ident: ast.get_child_ident("ident")?, + }, - "instantiation_expression" => { - FruExpression::Instantiation { - what: ast.parse_child_expression("what")?.wrap_box(), - args: ast.parse_child("args", parse_argument_list_instantiation)?, - } - } + "scope_expression" => FruExpression::ScopeAccessor, - "prop_access_expression" => { - FruExpression::PropAccess { - what: ast.parse_child_expression("what")?.wrap_box(), - ident: ast.get_child_ident("ident")?, - } - } + "function_expression" => FruExpression::Function { + args: ast.parse_child("parameters", parse_formal_parameters)?, + body: ast.parse_child("body", parse_function_body)?.wrap_rc(), + }, - "binary_expression" => { - FruExpression::Binary { - operator: ast.get_child_ident("operator")?, - left: ast.parse_child_expression("left")?.wrap_box(), - right: ast.parse_child_expression("right")?.wrap_box(), - } - } + "parenthesized_expression" => ast.parse_child_expression("expr")?, - "if_expression" => { - FruExpression::If { - condition: ast.parse_child_expression("condition")?.wrap_box(), - then_body: ast.parse_child_expression("then_body")?.wrap_box(), - else_body: ast.parse_child_expression("else_body")?.wrap_box(), - } - } + "block_expression" => FruExpression::Block { + body: ast.parse_children("body", parse_statement)?, + expr: ast.parse_child_expression("expr")?.wrap_box(), + }, + + "scope_modifier_expression" => FruExpression::ScopeModifier { + what: ast.parse_child_expression("what")?.wrap_box(), + body: ast.parse_children("body", parse_statement)?, + expr: ast.parse_child_expression("expr")?.wrap_box(), + }, + + "call_expression" => FruExpression::Call { + what: ast.parse_child_expression("what")?.wrap_box(), + args: ast.parse_child("args", parse_argument_list)?, + }, + + "curry_call_expression" => FruExpression::CurryCall { + what: ast.parse_child_expression("what")?.wrap_box(), + args: ast.parse_child("args", parse_argument_list)?, + }, + + "instantiation_expression" => FruExpression::Instantiation { + what: ast.parse_child_expression("what")?.wrap_box(), + args: ast.parse_child("args", parse_argument_list_instantiation)?, + }, + + "prop_access_expression" => FruExpression::PropAccess { + what: ast.parse_child_expression("what")?.wrap_box(), + ident: ast.get_child_ident("ident")?, + }, + + "binary_expression" => FruExpression::Binary { + operator: ast.get_child_ident("operator")?, + left: ast.parse_child_expression("left")?.wrap_box(), + right: ast.parse_child_expression("right")?.wrap_box(), + }, + + "if_expression" => FruExpression::If { + condition: ast.parse_child_expression("condition")?.wrap_box(), + then_body: ast.parse_child_expression("then_body")?.wrap_box(), + else_body: ast.parse_child_expression("else_body")?.wrap_box(), + }, + + "import_expression" => FruExpression::Import { + path: ast.parse_child_expression("path")?.wrap_box(), + }, unexpected => { return Err(ParseError::InvalidAst { @@ -525,11 +482,9 @@ fn parse_function_body(ast: NodeWrapper) -> Result { Ok(match ast.grammar_name() { "block_statement" => parse_statement(ast)?, - "block_expression" => { - FruStatement::Return { - value: Some(parse_expression(ast)?.wrap_box()), - } - } + "block_expression" => FruStatement::Return { + value: Some(parse_expression(ast)?.wrap_box()), + }, unexpected => { return Err(ParseError::InvalidAst { @@ -546,12 +501,10 @@ fn parse_type_member(ast: NodeWrapper) -> Result { "type_property" => parse_property(ast), - unexpected => { - Err(ParseError::InvalidAst { - position: ast.range(), - error: format!("Not a type member: {}", unexpected), - }) - } + unexpected => Err(ParseError::InvalidAst { + position: ast.range(), + error: format!("Not a type member: {}", unexpected), + }), } } @@ -702,18 +655,14 @@ fn parse_formal_parameter( match x.grammar_name() { "positional_parameter" => Ok((x.get_child_ident("ident")?, None)), - "default_parameter" => { - Ok(( - x.get_child_ident("ident")?, - Some(x.parse_child_expression("value")?), - )) - } - unexpected => { - Err(ParseError::InvalidAst { - position: x.range(), - error: format!("Not a formal parameter: {}", unexpected), - }) - } + "default_parameter" => Ok(( + x.get_child_ident("ident")?, + Some(x.parse_child_expression("value")?), + )), + unexpected => Err(ParseError::InvalidAst { + position: x.range(), + error: format!("Not a formal parameter: {}", unexpected), + }), } } @@ -765,18 +714,14 @@ fn parse_argument_item( match ast.grammar_name() { "positional_argument" => Ok((None, ast.parse_child_expression("value")?)), - "named_argument" => { - Ok(( - Some(ast.get_child_ident("ident")?), - ast.parse_child_expression("value")?, - )) - } + "named_argument" => Ok(( + Some(ast.get_child_ident("ident")?), + ast.parse_child_expression("value")?, + )), - unexpected => { - Err(ParseError::InvalidAst { - position: ast.range(), - error: format!("Not an argument: {}", unexpected), - }) - } + unexpected => Err(ParseError::InvalidAst { + position: ast.range(), + error: format!("Not an argument: {}", unexpected), + }), } } diff --git a/src/interpreter/value/fru_object.rs b/src/interpreter/value/fru_object.rs index 1bbfe50..c0e667f 100644 --- a/src/interpreter/value/fru_object.rs +++ b/src/interpreter/value/fru_object.rs @@ -124,12 +124,10 @@ impl FruObject { let tt = self.get_type().get_type_type(); match tt { - TypeType::Struct => { - FruObject::new_object( - self.get_type(), - self.internal.fields.borrow().iter().map(FruValue::fru_clone).collect(), - ) - } + TypeType::Struct => FruObject::new_object( + self.get_type(), + self.internal.fields.borrow().iter().map(FruValue::fru_clone).collect(), + ), TypeType::Class | TypeType::Data => FruValue::Object(self.clone()), } diff --git a/src/interpreter/value/fru_value.rs b/src/interpreter/value/fru_value.rs index c67aa68..962f69e 100644 --- a/src/interpreter/value/fru_value.rs +++ b/src/interpreter/value/fru_value.rs @@ -69,14 +69,12 @@ impl FruValue { )))) } - normal => { - Ok(FruValue::Function(AnyFunction::CurriedFunction(Rc::new( - CurriedFunction { - saved_args: args, - function: Rc::new(normal.clone()), - }, - )))) - } + normal => Ok(FruValue::Function(AnyFunction::CurriedFunction(Rc::new( + CurriedFunction { + saved_args: args, + function: Rc::new(normal.clone()), + }, + )))), } } @@ -92,12 +90,10 @@ impl FruValue { FruValue::NativeObject(obj) => obj.instantiate(args), - _ => { - FruError::new_res(format!( - "`{}` is not instantiatable", - self.get_type_identifier() - )) - } + _ => FruError::new_res(format!( + "`{}` is not instantiatable", + self.get_type_identifier() + )), } } @@ -109,12 +105,10 @@ impl FruValue { FruValue::NativeObject(obj) => obj.get_prop(ident), - _ => { - FruError::new_res(format!( - "cannot access prop of `{}`", - self.get_type_identifier() - )) - } + _ => FruError::new_res(format!( + "cannot access prop of `{}`", + self.get_type_identifier() + )), } } @@ -126,12 +120,10 @@ impl FruValue { FruValue::NativeObject(obj) => obj.set_prop(ident, value), - _ => { - FruError::new_res(format!( - "cannot set prop of `{}`", - self.get_type_identifier() - )) - } + _ => FruError::new_res(format!( + "cannot set prop of `{}`", + self.get_type_identifier() + )), } } diff --git a/src/interpreter/value/native/object.rs b/src/interpreter/value/native/object.rs index 2a5d5b6..5ff4559 100644 --- a/src/interpreter/value/native/object.rs +++ b/src/interpreter/value/native/object.rs @@ -1,13 +1,14 @@ +use std::any::Any; use std::rc::Rc; use crate::interpreter::{ - error::FruError, - identifier::Identifier, - value::fru_value::FruValue, + error::FruError, identifier::Identifier, value::fru_value::FruValue, value::function::EvaluatedArgumentList, }; pub trait INativeObject { + fn as_any(&self) -> &dyn Any; + fn get_type_identifier(&self) -> Identifier { Identifier::for_native_object() } @@ -86,4 +87,8 @@ impl NativeObject { pub fn fru_clone(&self) -> FruValue { FruValue::NativeObject(self.internal.fru_clone(self)) } + + pub fn downcast(&self) -> Option<&T> { + self.internal.as_any().downcast_ref::() + } } diff --git a/src/main.rs b/src/main.rs index c3fc662..ea224d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![feature(iterator_try_collect)] - use std::{path::PathBuf, time::Instant}; use crate::interpreter::runner::execute_file; diff --git a/src/stdlib/scope/fru_scope.rs b/src/stdlib/scope/fru_scope.rs index d57deab..376739c 100644 --- a/src/stdlib/scope/fru_scope.rs +++ b/src/stdlib/scope/fru_scope.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::{any::Any, rc::Rc}; use crate::interpreter::{ error::FruError, @@ -19,6 +19,10 @@ impl FruScope { } impl INativeObject for FruScope { + fn as_any(&self) -> &dyn Any { + self + } + fn get_type_identifier(&self) -> Identifier { Identifier::new("Scope") } @@ -28,8 +32,15 @@ impl INativeObject for FruScope { } fn set_prop(&self, ident: Identifier, value: FruValue) -> Result<(), FruError> { - self.scope - .set_variable(ident, value.clone()) - .or_else(|_| self.scope.let_variable(ident, value)) + self.scope.let_set_variable(ident, value); + Ok(()) + } +} + +pub fn extract_scope_from_value(v: &FruValue) -> Option> { + if let FruValue::NativeObject(o) = v { + o.downcast::().map(|x| x.scope.clone()) + } else { + None } } diff --git a/tests/expression/block_expression_tests.rs b/tests/expression/block_expression_tests.rs index de28acb..939009d 100644 --- a/tests/expression/block_expression_tests.rs +++ b/tests/expression/block_expression_tests.rs @@ -8,7 +8,7 @@ fn test_basics() { let x = x + 7; x * x }; - + assert_eq(y, 64); "#) } diff --git a/tests/expression/curry_call_expression_tests.rs b/tests/expression/curry_call_expression_tests.rs index c042804..5df1d17 100644 --- a/tests/expression/curry_call_expression_tests.rs +++ b/tests/expression/curry_call_expression_tests.rs @@ -4,13 +4,13 @@ use crate::run; fn test_curry_1() { run(r#" let f = fn (a, b, c) {a + b + c}; - + let g = f$(1); - + assert_eq(g(2, 3), 6); - + assert_eq(f(1, 2, 3), 6); - + assert_eq(g$(2)(5), 8); print(f); diff --git a/tests/expression/function_expression_tests.rs b/tests/expression/function_expression_tests.rs index fe03b78..379b11f 100644 --- a/tests/expression/function_expression_tests.rs +++ b/tests/expression/function_expression_tests.rs @@ -6,11 +6,11 @@ fn test_function() { let func = fn (x, y) { return x + y + {x * y}; }; - + let func_same = fn (x, y) { x + y + {x * y} }; - + assert_eq(func(1, 2), 5); assert_eq(func_same(5, 5), 35); "#) @@ -22,15 +22,15 @@ fn test_function_decorator() { let func = fn (x, y) { return x + y + {x * y}; }; - + let decorator = fn (func) { return fn (x, y) { return func(x - 1, y + 1) + 1; }; }; - + func = decorator(func); - + assert_eq(func(2, 1), 6); assert_eq(func(6, 4), 36); "#) @@ -41,12 +41,12 @@ fn test_function_nested() { run(r#" let comp = fn(x) { let tr1 = fn(y) { x + y }; - + let tr2 = fn(y) { x * y }; - + tr2(tr1(1)) }; - + assert_eq(comp(6), 42); "#) } @@ -81,13 +81,13 @@ fn test_eval_3() { fn test_overall() { run(r#" let f = fn (a, b) {a + b}; - + let dec = fn (func) { fn (w) { func$(w) } }; - + let g = dec(f); - + assert_eq(g(1)(2), 3); "#) } diff --git a/tests/expression/instantiation_expression_tests.rs b/tests/expression/instantiation_expression_tests.rs index 1e6322d..3e958c1 100644 --- a/tests/expression/instantiation_expression_tests.rs +++ b/tests/expression/instantiation_expression_tests.rs @@ -15,7 +15,7 @@ fn test_error_propagation_2() { struct Box { f; } - + let b = Box :{ 1 / 0 }; "#) } @@ -27,7 +27,7 @@ fn test_error_propagation_3() { struct Box { f; } - + Box :{ 1, 2 }; "#) } diff --git a/tests/expression/variable_expression_tests.rs b/tests/expression/variable_expression_tests.rs index 6ff2663..23f0b90 100644 --- a/tests/expression/variable_expression_tests.rs +++ b/tests/expression/variable_expression_tests.rs @@ -48,7 +48,7 @@ fn test_while() { let a = a - 4; s = s + a; } - + assert_eq(s, -6); "#) } diff --git a/tests/lib.rs b/tests/lib.rs index f33b611..2f3f73f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,5 +1,3 @@ -#![feature(iterator_try_collect)] - use std::io::Write; use tempfile::NamedTempFile; @@ -9,10 +7,14 @@ use crate::interpreter::{identifier::reset_poison, runner::execute_file}; #[path = "../src/interpreter/mod.rs"] mod interpreter; +#[path = "../src/stdlib/mod.rs"] +mod stdlib; + mod builtin; mod expression; mod literal_expression; mod oop; +mod scope_manipulation; mod statement; pub fn run(code: &str) { diff --git a/tests/literal_expression/literal_bool_tests.rs b/tests/literal_expression/literal_bool_tests.rs index 8945cbc..6c05383 100644 --- a/tests/literal_expression/literal_bool_tests.rs +++ b/tests/literal_expression/literal_bool_tests.rs @@ -7,7 +7,7 @@ fn test_basics() { let b = false; assert_eq(a, true); assert_eq(b, false); - + print(true); "#); } diff --git a/tests/oop/class_tests.rs b/tests/oop/class_tests.rs index c2052ce..b87aa17 100644 --- a/tests/oop/class_tests.rs +++ b/tests/oop/class_tests.rs @@ -6,17 +6,17 @@ fn test_class() { class Box { f; } - + let b = Box :{ f: 5 }; assert_eq(b.f, 5); - + b.f = 10; assert_eq(b.f, 10); - + let b2 = b; - + assert_eq(b2.f, 10); - + b2.f = 20; assert_eq(b.f, 20); assert_eq(b2.f, 20); diff --git a/tests/oop/data_tests.rs b/tests/oop/data_tests.rs index 0828dc7..da5b671 100644 --- a/tests/oop/data_tests.rs +++ b/tests/oop/data_tests.rs @@ -7,10 +7,10 @@ fn test_data() { data Box { f; } - + let b = Box :{ 5 }; assert_eq(b.f, 5); - + b.f = 10; "#) } diff --git a/tests/oop/general_tests.rs b/tests/oop/general_tests.rs index 0b4a7e2..62911e7 100644 --- a/tests/oop/general_tests.rs +++ b/tests/oop/general_tests.rs @@ -36,29 +36,29 @@ fn test_operators() { x; y; } - + operator + (a : Vec2, b : Vec2) { Vec2 :{ a.x + b.x, a.y + b.y } } - + commutative operator * (a : Vec2, k : Number) { Vec2 :{ a.x * k, a.y * k } } - + operator +-*/%=<>&|^!? (a : Number, k : Number) { a * k } - + let v1 = Vec2 :{ 1, 2 }; let v2 = Vec2 :{ 3, 4 }; - + assert_eq(v1 + v2, Vec2 :{ 4, 6 }); - + assert_eq(v1 * 2, Vec2 :{ 2, 4 }); assert_eq(5 * v1 * 2, Vec2 :{ 10, 20 }); - + assert_eq(6 +-*/%=<>&|^!? 9, 54); - + print(Vec2); "#) } diff --git a/tests/oop/property_tests.rs b/tests/oop/property_tests.rs index e0d7a07..85d3655 100644 --- a/tests/oop/property_tests.rs +++ b/tests/oop/property_tests.rs @@ -6,7 +6,7 @@ fn test_basics() { struct Vec { x; y; - + Length { get { (x * x + y * y) ** 0.5 } set(value) { @@ -16,12 +16,12 @@ fn test_basics() { } } } - + let v = Vec :{ x: 3, y: 4 }; assert_eq(v.Length, 5); - + v.Length = 1; - + assert_eq(v.x + v.y, 1.4); "#) } @@ -31,14 +31,14 @@ fn test_getter_arrow() { run(r#" struct Thing { x; - + Foo { get => x + 1; } } - + let t = Thing :{ x: 3 }; - + assert_eq(t.Foo, 4); "#) } @@ -48,23 +48,23 @@ fn test_other() { run(r#" struct Thing { x; - + Foo { set(val) { if val == 3 { return; } - + x = val * 2; } } } - + let t = Thing :{ x: 3 }; t.Foo = 5; assert_eq(t.x, 10); - + t.Foo = 3; assert_eq(t.x, 10); "#) @@ -77,9 +77,9 @@ fn test_no_getter() { struct Thing { X {} } - + let t = Thing :{}; - + t.X; "#) } @@ -91,13 +91,37 @@ fn test_no_setter() { struct Thing { X {} } - + let t = Thing :{}; - + t.X = 3; "#) } +#[test] +#[should_panic(expected = "static property `X` has no getter")] +fn test_static_no_getter() { + run(r#" + struct Thing { + static X {} + } + + Thing.X; + "#) +} + +#[test] +#[should_panic(expected = "static property `X` has no setter")] +fn test_static_no_setter() { + run(r#" + struct Thing { + static X {} + } + + Thing.X = 3; + "#) +} + #[test] #[should_panic(expected = "unexpected signal Continue")] fn test_unexpected_signal() { @@ -109,9 +133,27 @@ fn test_unexpected_signal() { } } } - + let t = Thing :{}; - + + t.X = 3; + "#) +} + +#[test] +#[should_panic(expected = "division by zero")] +fn test_error_propagation() { + run(r#" + struct Thing { + X { + set { + 1 / 0; + } + } + } + + let t = Thing :{}; + t.X = 3; "#) } @@ -121,7 +163,7 @@ fn test_static_basics() { run(r#" let T = { let inner = 5; - + struct Thing { static Foo { get => inner + 5; @@ -130,18 +172,18 @@ fn test_static_basics() { } } } - + Thing }; - + assert_eq(T.Foo, 10); - + T.Foo = 3; - + assert_eq(T.Foo, 3); - + let t = T :{}; - + assert_eq(t.Foo, 3); "#) } diff --git a/tests/oop/static_tests.rs b/tests/oop/static_tests.rs index 7fb114f..12d8644 100644 --- a/tests/oop/static_tests.rs +++ b/tests/oop/static_tests.rs @@ -26,35 +26,35 @@ fn test_vector() { x = y; y = tmp; } - + mul() { x = x * m; y = y * m; } - + static new45(x) { Vec2:{x * m, x * m} } } - + let v = Vec2 :{ 1, 2 }; - + v.swap(); assert_eq(v.x, 2); - + v.mul(); assert_eq(v.y, 10); - + Vec2.m = 14; - + let v2 = Vec2.new45(5); - + assert_eq(v2.y, 70); - + assert_eq(Vec2.other, nah); print(v, v2); - + v.other = 5; assert_eq(Vec2.other, 5); "#) diff --git a/tests/oop/struct_tests.rs b/tests/oop/struct_tests.rs index cae6135..b782e39 100644 --- a/tests/oop/struct_tests.rs +++ b/tests/oop/struct_tests.rs @@ -6,17 +6,17 @@ fn test_box() { struct Box { f; } - + let b = Box :{ 5 }; assert_eq(b.f, 5); - + b.f = 10; assert_eq(b.f, 10); - + let b2 = b; - + assert_eq(b2.f, 10); - + b2.f = 20; assert_eq(b.f, 10); assert_eq(b2.f, 20); diff --git a/tests/scope_manipulation/basics.rs b/tests/scope_manipulation/basics.rs new file mode 100644 index 0000000..8480fc3 --- /dev/null +++ b/tests/scope_manipulation/basics.rs @@ -0,0 +1,79 @@ +use crate::run; + +#[test] +fn test_basics() { + run(r#" + let f1 = fn() { + let a = 5; + let b = 3; + scope() + }; + + let s = f1(); + + scope s { + let c = a + b; + assert_eq(c * a, 40); + } + + assert_eq(s.c, 8); + + assert_eq(scope s { + c = c * c; + c + 1 + }, 65); + + s.w = 1; + s.w = s.w + 1; + + assert_eq(s.w, 2); + "#) +} + +#[test] +#[should_panic(expected = "Expected `Scope` in scope modifier statement, got `Number`")] +fn test_unexpected_type_1() { + run(r#" + scope 1 {} + "#) +} + +#[test] +#[should_panic(expected = "Expected `Scope` in scope modifier expression, got `Number`")] +fn test_unexpected_type_2() { + run(r#" + scope 1 { nah }; + "#) +} + +#[test] +#[should_panic(expected = "division by zero")] +fn test_error_propagation_1() { + run(r#" + scope 1 / 0 {} + "#) +} + +#[test] +#[should_panic(expected = "division by zero")] +fn test_error_propagation_2() { + run(r#" + scope 1 / 0 { 1 }; + "#) +} + +#[test] +#[should_panic(expected = "division by zero")] +fn test_error_propagation_3() { + run(r#" + scope scope() { 1 / 0; } + "#) +} + +#[test] +#[should_panic(expected = "division by zero")] +fn test_error_propagation_4() { + run(r#" + scope scope() { 1 / 0; nah }; + "#) +} diff --git a/tests/scope_manipulation/mod.rs b/tests/scope_manipulation/mod.rs new file mode 100644 index 0000000..40db6c0 --- /dev/null +++ b/tests/scope_manipulation/mod.rs @@ -0,0 +1 @@ +mod basics; From 71e16eca234683c3c34271c5c9b0d090b7e273a7 Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Thu, 13 Jun 2024 21:13:27 +0300 Subject: [PATCH 3/6] docs: improved a couple of sentences feat: add api to run source code from string goals: refine tasks and state problems refactor: replace tuples by appropriate structs --- docs/src/02-common-concepts/06-currying.md | 2 +- .../06-properties.md | 4 +- obsidian/TODO.canvas | 20 +++--- obsidian/TOTHINK.canvas | 3 +- src/interpreter/ast_helpers.rs | 20 ++++++ src/interpreter/error.rs | 1 - src/interpreter/expression.rs | 2 +- src/interpreter/identifier.rs | 6 -- src/interpreter/mod.rs | 1 + src/interpreter/runner.rs | 4 ++ src/interpreter/statement.rs | 30 +++++---- src/interpreter/tree_sitter_parser.rs | 61 +++++++++---------- src/interpreter/value/fru_object.rs | 4 +- src/interpreter/value/fru_type.rs | 2 +- src/interpreter/value/fru_value.rs | 4 +- src/interpreter/value/function.rs | 4 +- tests/lib.rs | 15 +---- 17 files changed, 93 insertions(+), 90 deletions(-) create mode 100644 src/interpreter/ast_helpers.rs diff --git a/docs/src/02-common-concepts/06-currying.md b/docs/src/02-common-concepts/06-currying.md index ebec343..3c599cc 100644 --- a/docs/src/02-common-concepts/06-currying.md +++ b/docs/src/02-common-concepts/06-currying.md @@ -1,6 +1,6 @@ # Currying -You can apply first n arguments to a function to obtain a new function that accepts the rest of the arguments. This is called currying. Curried can be curried as many times as you want. +You can apply first n arguments to a function to make a new function that accepts the rest of the arguments. This is called currying. Curried can be curried as many times as you want. ```frugurt let add = fn(a, b) { diff --git a/docs/src/03-object-oriented-programming/06-properties.md b/docs/src/03-object-oriented-programming/06-properties.md index ede9195..e8871c4 100644 --- a/docs/src/03-object-oriented-programming/06-properties.md +++ b/docs/src/03-object-oriented-programming/06-properties.md @@ -25,7 +25,7 @@ v.Length = 1; print(v); // Vec { x: 0.6, y: 0.8 } ``` -In this example, Vec has a "property" Length, that is freely computable from other fields. +In this example, Vec has a "property" Length that is easily computable from other fields. Like methods, properties can access fields, methods and other properties of the object. `(new_length)` can be omitted, in which case the default identifier `value` is used. Properties can be static. @@ -44,5 +44,5 @@ print(Time.Now); ``` In this example, imagine game engine. -Static field time is updated by game engine every frame, and public property `Now` can be used to obtain current time +Static field time is updated by game engine every frame, and public property `Now` can be used to get current time on the user side. diff --git a/obsidian/TODO.canvas b/obsidian/TODO.canvas index 2e12458..3151bc6 100644 --- a/obsidian/TODO.canvas +++ b/obsidian/TODO.canvas @@ -1,19 +1,20 @@ { "nodes":[ - {"id":"0c274527d795ce38","x":-80,"y":600,"width":400,"height":400,"type":"group","label":"OPTIMIZATIONS"}, + {"id":"0c274527d795ce38","type":"group","x":-80,"y":600,"width":400,"height":400,"label":"OPTIMIZATIONS"}, {"id":"0e8289b07d6ca556","type":"group","x":-600,"y":600,"width":400,"height":380,"label":"CI/CD"}, - {"id":"b9c4b54397d2bf2d","x":-43,"y":666,"width":250,"height":87,"type":"text","text":"macro for computing hash of ident in compile time"}, - {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":-580,"y":640,"width":250,"height":50}, {"id":"070a7b543247f7a2","type":"text","text":"simplified syntax for read-only properties","x":-290,"y":360,"width":250,"height":60}, {"id":"5d4e5bd937436926","type":"text","text":"computed properties","x":-640,"y":360,"width":250,"height":60,"color":"4"}, {"id":"18df0d00841f02bd","type":"text","text":"collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-80,"y":10,"width":250,"height":182,"color":"3"}, - {"id":"97a68fbd25b52ede","x":260,"y":-280,"width":250,"height":60,"type":"text","text":"destructuring let statements"}, - {"id":"1c18b7117da43e6e","type":"text","text":"\"evil\" features","x":-460,"y":-180,"width":250,"height":67,"color":"5"}, + {"id":"1c18b7117da43e6e","type":"text","text":"\"evil\" features","x":-460,"y":-180,"width":250,"height":67,"color":"4"}, {"id":"41499d9f6eafdeec","type":"text","text":"trait polymorphism for native types","x":-460,"y":-40,"width":250,"height":60,"color":"4"}, - {"id":"4ab66adbfddd4d15","type":"text","text":"scope manipulation features","x":-80,"y":-180,"width":250,"height":60}, - {"id":"feb6594a9d261a54","type":"text","text":"modules and imports","x":260,"y":-180,"width":250,"height":60}, - {"id":"545169f683d1ebef","x":620,"y":-180,"width":250,"height":60,"type":"text","text":"import as expression"}, - {"id":"550b9c0e5c7cd03a","type":"text","text":"derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":620,"y":300,"width":250,"height":160} + {"id":"4ab66adbfddd4d15","type":"text","text":"scope manipulation features","x":-80,"y":-180,"width":250,"height":60,"color":"4"}, + {"id":"feb6594a9d261a54","type":"text","text":"modules and imports","x":260,"y":-180,"width":250,"height":60,"color":"4"}, + {"id":"545169f683d1ebef","type":"text","text":"import as expression","x":620,"y":-180,"width":250,"height":60,"color":"4"}, + {"id":"550b9c0e5c7cd03a","type":"text","text":"derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":620,"y":300,"width":250,"height":160}, + {"id":"97a68fbd25b52ede","type":"text","text":"destructuring let statements","x":620,"y":-280,"width":250,"height":60}, + {"id":"b9c4b54397d2bf2d","type":"text","text":"macro for computing hash of ident in compile time","x":-43,"y":666,"width":250,"height":87}, + {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":-580,"y":640,"width":250,"height":60}, + {"id":"aa2bea5c494cbddd","x":-160,"y":252,"width":250,"height":60,"type":"text","text":"add traceback for errors"} ], "edges":[ {"id":"4f43eea514ca8881","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, @@ -21,7 +22,6 @@ {"id":"e3e0be30be175453","fromNode":"1c18b7117da43e6e","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, {"id":"8706d0e0442108f4","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"18df0d00841f02bd","toSide":"left"}, {"id":"10698732105d19f9","fromNode":"5d4e5bd937436926","fromSide":"right","toNode":"070a7b543247f7a2","toSide":"left"}, - {"id":"889e945801477a2c","fromNode":"97a68fbd25b52ede","fromSide":"right","toNode":"545169f683d1ebef","toSide":"left"}, {"id":"3f046cb411b32fae","fromNode":"feb6594a9d261a54","fromSide":"right","toNode":"545169f683d1ebef","toSide":"left"} ] } \ No newline at end of file diff --git a/obsidian/TOTHINK.canvas b/obsidian/TOTHINK.canvas index 23bd399..517614c 100644 --- a/obsidian/TOTHINK.canvas +++ b/obsidian/TOTHINK.canvas @@ -1,6 +1,7 @@ { "nodes":[ - {"id":"901340d58aa31311","x":-620,"y":-360,"width":250,"height":130,"type":"text","text":"introduce new wrapper extensions like `wrap_ok`, `wrap_err`, `wrap_value`"} + {"id":"1ac37b84ff817ecc","x":-733,"y":-605,"width":473,"height":165,"type":"text","text":"Issue 1:\nIf curried function mutates one of it's arguments, should subsequent calls receive mutated value or should it be cloned on call?"}, + {"id":"901340d58aa31311","type":"text","text":"introduce new wrapper extensions like `wrap_ok`, `wrap_err`, `wrap_value`","x":-720,"y":-220,"width":250,"height":130} ], "edges":[] } \ No newline at end of file diff --git a/src/interpreter/ast_helpers.rs b/src/interpreter/ast_helpers.rs new file mode 100644 index 0000000..6a3490c --- /dev/null +++ b/src/interpreter/ast_helpers.rs @@ -0,0 +1,20 @@ +use std::rc::Rc; + +use crate::interpreter::{ + expression::FruExpression, identifier::Identifier, statement::FruStatement, + value::function::FormalParameters, +}; + +#[derive(Debug, Clone)] +pub struct RawStaticField { + pub ident: Identifier, + pub value: Option>, +} + +#[derive(Debug, Clone)] +pub struct RawMethod { + pub is_static: bool, + pub ident: Identifier, + pub parameters: FormalParameters, + pub body: Rc, +} diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index e911f6f..86875d2 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -1,4 +1,3 @@ -// TODO: make use of this use thiserror::Error; use crate::interpreter::value::function::ArgumentError; diff --git a/src/interpreter/expression.rs b/src/interpreter/expression.rs index a783c8c..5907ab2 100644 --- a/src/interpreter/expression.rs +++ b/src/interpreter/expression.rs @@ -86,7 +86,7 @@ impl FruExpression { FruExpression::ScopeAccessor => Ok(FruScope::new_value(scope)), FruExpression::Function { args, body } => Ok(FruFunction { - argument_idents: args.clone(), + parameters: args.clone(), body: body.clone(), scope: scope.clone(), } diff --git a/src/interpreter/identifier.rs b/src/interpreter/identifier.rs index a6f225c..c92612a 100644 --- a/src/interpreter/identifier.rs +++ b/src/interpreter/identifier.rs @@ -21,12 +21,6 @@ pub struct OperatorIdentifier { right: Identifier, } -pub fn reset_poison() { - if BACKWARDS_MAP.lock().is_err() { - BACKWARDS_MAP.clear_poison() - } -} - impl Identifier { pub fn new(ident: &str) -> Self { let mut hasher = DefaultHasher::new(); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 29af3b8..78fe709 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,3 +1,4 @@ +mod ast_helpers; pub mod builtins; pub mod control; pub mod error; diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 520ae1f..b0d93da 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -6,6 +6,10 @@ pub fn execute_file(path: &Path) -> Result, FruError> { let source_code = read_to_string(path) .map_err(|err| FruError::new(format!("Error reading file {path:?} {err}")))?; + execute_source_code(source_code) +} + +pub fn execute_source_code(source_code: String) -> Result, FruError> { let ast = match tree_sitter_parser::parse(source_code) { Ok(ast) => ast, Err(err) => return Err(FruError::new(err.to_string())), diff --git a/src/interpreter/statement.rs b/src/interpreter/statement.rs index 01e3b48..ee846fa 100644 --- a/src/interpreter/statement.rs +++ b/src/interpreter/statement.rs @@ -1,20 +1,18 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; use crate::interpreter::{ + ast_helpers::{RawMethod, RawStaticField}, control::Control, expression::FruExpression, identifier::{Identifier, OperatorIdentifier}, scope::Scope, value::fru_type::{FruField, FruType, Property, TypeType}, value::fru_value::FruValue, - value::function::{FormalParameters, FruFunction}, + value::function::FruFunction, value::operator::AnyOperator, }; use crate::stdlib::scope::fru_scope::extract_scope_from_value; -// TODO: make normal struct -pub type RawMethods = Vec<(bool, Identifier, FormalParameters, Rc)>; - #[derive(Debug, Clone)] pub enum FruStatement { SourceCode { @@ -70,10 +68,10 @@ pub enum FruStatement { type_type: TypeType, ident: Identifier, fields: Vec, - static_fields: Vec<(FruField, Option>)>, + static_fields: Vec, properties: HashMap, static_properties: HashMap, - methods: RawMethods, + methods: Vec, }, } @@ -236,28 +234,28 @@ impl FruStatement { let mut methods_ = HashMap::new(); let mut static_methods_ = HashMap::new(); - for (is_static, ident, arg_list, body) in methods { - let mt = FruFunction { - argument_idents: arg_list.clone(), - body: body.clone(), + for method in methods { + let function = FruFunction { + parameters: method.parameters.clone(), + body: method.body.clone(), scope: scope.clone(), }; - if *is_static { - static_methods_.insert(*ident, mt); + if method.is_static { + static_methods_.insert(method.ident, function); } else { - methods_.insert(*ident, mt); + methods_.insert(method.ident, function); } } let mut static_fields_evaluated = HashMap::new(); - for (field, value) in static_fields { - let value = if let Some(v) = value { + for static_field in static_fields { + let value = if let Some(v) = &static_field.value { v.evaluate(scope.clone())? } else { FruValue::Nah }; - static_fields_evaluated.insert(field.ident, value); + static_fields_evaluated.insert(static_field.ident, value); } scope.let_variable( diff --git a/src/interpreter/tree_sitter_parser.rs b/src/interpreter/tree_sitter_parser.rs index 7debee4..9b05149 100644 --- a/src/interpreter/tree_sitter_parser.rs +++ b/src/interpreter/tree_sitter_parser.rs @@ -6,14 +6,17 @@ use tree_sitter::{Node, Parser, Range}; use tree_sitter_frugurt; use crate::interpreter::{ + ast_helpers::{RawMethod, RawStaticField}, expression::FruExpression, helpers::WrappingExtension, identifier::Identifier, - statement::{FruStatement, RawMethods}, - value::fru_type::FruField, - value::fru_type::{Property, TypeType}, - value::fru_value::FruValue, - value::function::{ArgumentList, FormalParameters}, + statement::FruStatement, + value::{ + fru_type::{Property, TypeType}, + fru_type::FruField, + fru_value::FruValue, + function::{ArgumentList, FormalParameters} + } }; #[derive(Error, Debug)] @@ -78,7 +81,7 @@ pub enum ParseError { enum TypeMember { NormalField(FruField), - StaticField((FruField, Option>)), + StaticField(RawStaticField), Property(Property), StaticProperty(Property), } @@ -522,16 +525,17 @@ fn parse_field(ast: NodeWrapper) -> Result { }); } - let res = FruField { - is_public, - ident, - type_ident, - }; - Ok(if is_static { - TypeMember::StaticField((res, value.map(Box::new))) + TypeMember::StaticField(RawStaticField { + ident, + value: value.map(Box::new), + }) } else { - TypeMember::NormalField(res) + TypeMember::NormalField(FruField { + is_public, + ident, + type_ident, + }) }) } @@ -541,7 +545,7 @@ fn parse_property(ast: NodeWrapper) -> Result { Set((Identifier, Rc), NodeWrapper<'a>), } - // TODO: add static and public modifiers + // TODO: add public modifier let ident = ast.get_child_ident("ident")?; let is_static = ast.get_child("static").is_ok(); @@ -607,22 +611,17 @@ fn parse_property(ast: NodeWrapper) -> Result { }) } -fn parse_impl(ast: NodeWrapper) -> Result { +fn parse_impl(ast: NodeWrapper) -> Result, ParseError> { ast.parse_children("methods", parse_method) } -fn parse_method( - ast: NodeWrapper, -) -> Result<(bool, Identifier, FormalParameters, Rc), ParseError> { - let is_static = ast.get_child("static").is_ok(); - - let ident = ast.get_child_ident("ident")?; - - let args = ast.parse_child("parameters", parse_formal_parameters)?; - - let body = ast.parse_child("body", parse_function_body)?.wrap_rc(); - - Ok((is_static, ident, args, body)) +fn parse_method(ast: NodeWrapper) -> Result { + Ok(RawMethod { + is_static: ast.get_child("static").is_ok(), + ident: ast.get_child_ident("ident")?, + parameters: ast.parse_child("parameters", parse_formal_parameters)?, + body: ast.parse_child("body", parse_function_body)?.wrap_rc(), + }) } fn parse_formal_parameters(ast: NodeWrapper) -> Result { @@ -659,6 +658,7 @@ fn parse_formal_parameter( x.get_child_ident("ident")?, Some(x.parse_child_expression("value")?), )), + unexpected => Err(ParseError::InvalidAst { position: x.range(), error: format!("Not a formal parameter: {}", unexpected), @@ -671,9 +671,8 @@ fn parse_argument_list(ast: NodeWrapper) -> Result { let mut was_named = false; - for i in 0..args.len() { - // FIXME - if args[i].0.is_some() { + for (i, (name, _)) in args.iter().enumerate() { + if name.is_some() { was_named = true; } else if was_named { return Err(ParseError::Error { diff --git a/src/interpreter/value/fru_object.rs b/src/interpreter/value/fru_object.rs index c0e667f..090c116 100644 --- a/src/interpreter/value/fru_object.rs +++ b/src/interpreter/value/fru_object.rs @@ -63,13 +63,13 @@ impl FruObject { } if let Some(FruFunction { - argument_idents, + parameters: argument_idents, body, .. }) = self.get_type().get_method(ident) { return Ok(FruFunction { - argument_idents, + parameters: argument_idents, body, scope: Scope::new_with_object(self.clone()), } diff --git a/src/interpreter/value/fru_type.rs b/src/interpreter/value/fru_type.rs index 8a57fd8..c2241ce 100644 --- a/src/interpreter/value/fru_type.rs +++ b/src/interpreter/value/fru_type.rs @@ -132,7 +132,7 @@ impl FruType { if let Some(static_method) = self.internal.static_methods.get(&ident) { return Ok(FruFunction { - argument_idents: static_method.argument_idents.clone(), + parameters: static_method.parameters.clone(), body: static_method.body.clone(), scope: Scope::new_with_type(self.clone()), } diff --git a/src/interpreter/value/fru_value.rs b/src/interpreter/value/fru_value.rs index 962f69e..2492213 100644 --- a/src/interpreter/value/fru_value.rs +++ b/src/interpreter/value/fru_value.rs @@ -54,11 +54,9 @@ impl FruValue { pub fn curry_call(&self, args: EvaluatedArgumentList) -> Result { match self { FruValue::Function(func) => { - // TODO: test compatibility - match func { AnyFunction::CurriedFunction(func) => { - let mut new_args = func.saved_args.clone(); // TODO: fru_clone()? + let mut new_args = func.saved_args.clone(); // TODO: obsidian Issue 1 new_args.args.extend(args.args); Ok(FruValue::Function(AnyFunction::CurriedFunction(Rc::new( diff --git a/src/interpreter/value/function.rs b/src/interpreter/value/function.rs index 8c238a2..40a232d 100644 --- a/src/interpreter/value/function.rs +++ b/src/interpreter/value/function.rs @@ -33,7 +33,7 @@ pub enum AnyFunction { #[derive(Clone)] pub struct FruFunction { - pub argument_idents: FormalParameters, + pub parameters: FormalParameters, pub body: Rc, pub scope: Rc, } @@ -77,7 +77,7 @@ impl FruFunction { fn call(&self, args: EvaluatedArgumentList) -> Result { let new_scope = Scope::new_with_parent(self.scope.clone()); - self.argument_idents.apply(args, new_scope.clone())?; + self.parameters.apply(args, new_scope.clone())?; returned_unit(self.body.execute(new_scope)) } diff --git a/tests/lib.rs b/tests/lib.rs index 2f3f73f..66a2234 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,8 +1,4 @@ -use std::io::Write; - -use tempfile::NamedTempFile; - -use crate::interpreter::{identifier::reset_poison, runner::execute_file}; +use crate::interpreter::runner::execute_source_code; #[path = "../src/interpreter/mod.rs"] mod interpreter; @@ -18,14 +14,7 @@ mod scope_manipulation; mod statement; pub fn run(code: &str) { - let mut file = NamedTempFile::new().expect("failed to create temporary file"); - - file.write_all(code.as_bytes()).expect("failed to write to temporary file"); - file.flush().unwrap(); - - reset_poison(); - - if let Err(err) = execute_file(file.path()) { + if let Err(err) = execute_source_code(code.to_owned()) { panic!("{}", err) } } From 96c8024e7fce908d8ab9f4546668ee95a78c909f Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Sat, 15 Jun 2024 15:17:39 +0300 Subject: [PATCH 4/6] perf: make static Identifiers compute hash at compile time, increase performance up tp 40% refactor: make static Identifiers constants, not functions --- Cargo.toml | 2 + .../src/01-getting-started/02-installation.md | 2 +- .../06-properties.md | 2 +- macros/.gitignore | 2 + macros/Cargo.toml | 11 ++ macros/src/lib.rs | 27 ++++ obsidian/TODO.canvas | 16 +-- src/interpreter/builtins/operators.rs | 72 +++++----- src/interpreter/identifier.rs | 133 +++++------------- src/interpreter/tree_sitter_parser.rs | 10 +- src/interpreter/value/fru_value.rs | 23 +-- src/interpreter/value/native/object.rs | 19 +-- src/stdlib/scope/fru_scope.rs | 8 +- tests/oop/general_tests.rs | 2 +- tests/oop/property_tests.rs | 2 +- 15 files changed, 164 insertions(+), 167 deletions(-) create mode 100644 macros/.gitignore create mode 100644 macros/Cargo.toml create mode 100644 macros/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 101275c..c6cba38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ tree-sitter = "0.22.5" #tree-sitter-frugurt = "0.0.10" tree-sitter-frugurt = { path = "../tree-sitter-frugurt" } #uid = "0.1.7" +macros = {path = "./macros"} +ctor = "0.2.8" [dev-dependencies] tempfile = "3.10.1" diff --git a/docs/src/01-getting-started/02-installation.md b/docs/src/01-getting-started/02-installation.md index ff02d34..45e63cf 100644 --- a/docs/src/01-getting-started/02-installation.md +++ b/docs/src/01-getting-started/02-installation.md @@ -10,4 +10,4 @@ You can download if from [release page](https://github.com/frugurt-lang/frugurt/ You can build Frugurt from [source code](https://github.com/frugurt-lang/frugurt) on any platform. -Use [Rust Toolchain](https://www.rust-lang.org/tools/install) to build interpreter. +Use [Rust Toolchain](https://www.rust-lang.org/tools/install) to build interpreter. diff --git a/docs/src/03-object-oriented-programming/06-properties.md b/docs/src/03-object-oriented-programming/06-properties.md index e8871c4..dcbe754 100644 --- a/docs/src/03-object-oriented-programming/06-properties.md +++ b/docs/src/03-object-oriented-programming/06-properties.md @@ -34,7 +34,7 @@ Also, there is no need to implement `get` and `set` every time. ```frugurt class Time { static time = 0; - + pub static Now { get { time } // this is equivalent to `get => time;` } diff --git a/macros/.gitignore b/macros/.gitignore new file mode 100644 index 0000000..ca98cd9 --- /dev/null +++ b/macros/.gitignore @@ -0,0 +1,2 @@ +/target/ +Cargo.lock diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..d3e27e1 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = "2.0.66" +quote = "1.0.36" diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..762289b --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,27 @@ +use proc_macro::TokenStream; +use std::hash::{DefaultHasher, Hash, Hasher}; + +use quote::quote; +use syn::LitStr; + +#[proc_macro] +pub fn static_ident(input: TokenStream) -> TokenStream { + let ast: LitStr = syn::parse(input).unwrap(); + + let ident = ast.value(); + + let mut hasher = DefaultHasher::new(); + ident.hash(&mut hasher); + let hashed_ident = hasher.finish(); + + quote! { + { + #[ctor::ctor] + fn ident_ctor() { + Identifier::new(#ident); + } + Identifier::new_unchecked(#hashed_ident) + } + } + .into() +} diff --git a/obsidian/TODO.canvas b/obsidian/TODO.canvas index 3151bc6..da854cb 100644 --- a/obsidian/TODO.canvas +++ b/obsidian/TODO.canvas @@ -4,24 +4,22 @@ {"id":"0e8289b07d6ca556","type":"group","x":-600,"y":600,"width":400,"height":380,"label":"CI/CD"}, {"id":"070a7b543247f7a2","type":"text","text":"simplified syntax for read-only properties","x":-290,"y":360,"width":250,"height":60}, {"id":"5d4e5bd937436926","type":"text","text":"computed properties","x":-640,"y":360,"width":250,"height":60,"color":"4"}, - {"id":"18df0d00841f02bd","type":"text","text":"collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-80,"y":10,"width":250,"height":182,"color":"3"}, {"id":"1c18b7117da43e6e","type":"text","text":"\"evil\" features","x":-460,"y":-180,"width":250,"height":67,"color":"4"}, {"id":"41499d9f6eafdeec","type":"text","text":"trait polymorphism for native types","x":-460,"y":-40,"width":250,"height":60,"color":"4"}, {"id":"4ab66adbfddd4d15","type":"text","text":"scope manipulation features","x":-80,"y":-180,"width":250,"height":60,"color":"4"}, - {"id":"feb6594a9d261a54","type":"text","text":"modules and imports","x":260,"y":-180,"width":250,"height":60,"color":"4"}, - {"id":"545169f683d1ebef","type":"text","text":"import as expression","x":620,"y":-180,"width":250,"height":60,"color":"4"}, + {"id":"aa2bea5c494cbddd","type":"text","text":"add traceback for errors","x":-160,"y":252,"width":250,"height":60}, {"id":"550b9c0e5c7cd03a","type":"text","text":"derivation and implicit derivation, and make them overridable (the main reason is equality of objects)","x":620,"y":300,"width":250,"height":160}, - {"id":"97a68fbd25b52ede","type":"text","text":"destructuring let statements","x":620,"y":-280,"width":250,"height":60}, - {"id":"b9c4b54397d2bf2d","type":"text","text":"macro for computing hash of ident in compile time","x":-43,"y":666,"width":250,"height":87}, - {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":-580,"y":640,"width":250,"height":60}, - {"id":"aa2bea5c494cbddd","x":-160,"y":252,"width":250,"height":60,"type":"text","text":"add traceback for errors"} + {"id":"97a68fbd25b52ede","type":"text","text":"destructuring let statements","x":800,"y":160,"width":250,"height":60}, + {"id":"feb6594a9d261a54","type":"text","text":"modules and imports\n- [ ] tests\n- [x] import as expression","x":260,"y":-180,"width":250,"height":120,"color":"3"}, + {"id":"18df0d00841f02bd","type":"text","text":"collections\n- [ ] list\n- [ ] set\n- [ ] map\n- [ ] tuple?","x":-80,"y":0,"width":250,"height":182,"color":"3"}, + {"id":"b9c4b54397d2bf2d","type":"text","text":"macro for computing hash of ident in compile time","x":-40,"y":660,"width":250,"height":87,"color":"4"}, + {"id":"b9e932828d9a3c13","type":"text","text":"make cd for windows and linux releases","x":-580,"y":640,"width":250,"height":60} ], "edges":[ {"id":"4f43eea514ca8881","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, {"id":"94361d23f182cbe8","fromNode":"4ab66adbfddd4d15","fromSide":"right","toNode":"feb6594a9d261a54","toSide":"left"}, {"id":"e3e0be30be175453","fromNode":"1c18b7117da43e6e","fromSide":"right","toNode":"4ab66adbfddd4d15","toSide":"left"}, {"id":"8706d0e0442108f4","fromNode":"41499d9f6eafdeec","fromSide":"right","toNode":"18df0d00841f02bd","toSide":"left"}, - {"id":"10698732105d19f9","fromNode":"5d4e5bd937436926","fromSide":"right","toNode":"070a7b543247f7a2","toSide":"left"}, - {"id":"3f046cb411b32fae","fromNode":"feb6594a9d261a54","fromSide":"right","toNode":"545169f683d1ebef","toSide":"left"} + {"id":"10698732105d19f9","fromNode":"5d4e5bd937436926","fromSide":"right","toNode":"070a7b543247f7a2","toSide":"left"} ] } \ No newline at end of file diff --git a/src/interpreter/builtins/operators.rs b/src/interpreter/builtins/operators.rs index 69a1439..6852b5a 100644 --- a/src/interpreter/builtins/operators.rs +++ b/src/interpreter/builtins/operators.rs @@ -2,9 +2,8 @@ use std::collections::HashMap; use crate::interpreter::{ error::FruError, - identifier::{Identifier as Id, OperatorIdentifier as OpId}, - value::fru_value::FruValue, - value::operator::AnyOperator, + identifier::{id, OperatorIdentifier}, + value::{fru_value::FruValue, operator::AnyOperator}, }; macro_rules! builtin_operator { @@ -20,11 +19,11 @@ macro_rules! builtin_operator { } macro_rules! operator_group { - ($ident1:expr, $ident2:expr, [$(($op:ident, $fn_name:ident)),*]) => { + ($ident1:ident, $ident2:ident, [$(($op:ident, $fn_name:ident)),*]) => { [ $( ( - OpId::new(Id::$op(), $ident1, $ident2), + OperatorIdentifier::new(id::$op, id::$ident1, id::$ident2), AnyOperator::BuiltinOperator($fn_name), ) ),* @@ -32,53 +31,55 @@ macro_rules! operator_group { }; } -pub fn builtin_operators() -> HashMap { - let mut res = HashMap::from(operator_group!( - Id::for_number(), - Id::for_number(), +pub fn builtin_operators() -> HashMap { + let mut res = HashMap::new(); + + res.extend(operator_group!( + NUMBER, + NUMBER, [ - (for_plus, num_plus_num), - (for_minus, num_minus_num), - (for_multiply, num_mul_num), - (for_divide, num_div_num), - (for_mod, num_mod_num), - (for_pow, num_pow_num), - (for_less, num_less_num), - (for_less_eq, num_less_eq_num), - (for_greater, num_greater_num), - (for_greater_eq, num_greater_eq_num), - (for_eq, num_eq_num), - (for_not_eq, num_not_eq_num) + (PLUS, num_plus_num), + (MINUS, num_minus_num), + (MULTIPLY, num_mul_num), + (DIVIDE, num_div_num), + (MOD, num_mod_num), + (POW, num_pow_num), + (LESS, num_less_num), + (LESS_EQ, num_less_eq_num), + (GREATER, num_greater_num), + (GREATER_EQ, num_greater_eq_num), + (EQ, num_eq_num), + (NOT_EQ, num_not_eq_num) ] )); res.extend(operator_group!( - Id::for_bool(), - Id::for_bool(), - [(for_and, bool_and_bool), (for_or, bool_or_bool)] + BOOL, + BOOL, + [(AND, bool_and_bool), (OR, bool_or_bool)] )); res.extend(operator_group!( - Id::for_string(), - Id::for_string(), + STRING, + STRING, [ - (for_combine, string_concat), - (for_less, string_less_string), - (for_less_eq, string_less_eq_string), - (for_greater, string_greater_string), - (for_greater_eq, string_greater_eq_string), - (for_eq, string_eq_string), - (for_not_eq, string_not_eq_string) + (COMBINE, string_concat), + (LESS, string_less_string), + (LESS_EQ, string_less_eq_string), + (GREATER, string_greater_string), + (GREATER_EQ, string_greater_eq_string), + (EQ, string_eq_string), + (NOT_EQ, string_not_eq_string) ] )); res.extend([ ( - OpId::new(Id::for_multiply(), Id::for_string(), Id::for_number()), + OperatorIdentifier::new(id::MULTIPLY, id::STRING, id::NUMBER), AnyOperator::BuiltinOperator(string_mul_num), ), ( - OpId::new(Id::for_multiply(), Id::for_number(), Id::for_string()), + OperatorIdentifier::new(id::MULTIPLY, id::NUMBER, id::STRING), AnyOperator::BuiltinOperator(num_mul_string), ), ]); @@ -137,6 +138,7 @@ builtin_operator!(string_greater_string, String, String, Bool, >); builtin_operator!(string_greater_eq_string, String, String, Bool, >=); builtin_operator!(string_eq_string, String, String, Bool, ==); builtin_operator!(string_not_eq_string, String, String, Bool, !=); + fn string_concat(left: FruValue, right: FruValue) -> Result { if let (FruValue::String(l), FruValue::String(r)) = (left, right) { return Ok(FruValue::String(l + &*r)); diff --git a/src/interpreter/identifier.rs b/src/interpreter/identifier.rs index c92612a..8ed967a 100644 --- a/src/interpreter/identifier.rs +++ b/src/interpreter/identifier.rs @@ -1,5 +1,7 @@ use std::{ - collections::HashMap, fmt::Debug, fmt::Display, hash::DefaultHasher, hash::Hash, hash::Hasher, + collections::HashMap, + fmt::{Debug, Display}, + hash::{DefaultHasher, Hash, Hasher}, sync::Mutex, }; @@ -24,9 +26,7 @@ pub struct OperatorIdentifier { impl Identifier { pub fn new(ident: &str) -> Self { let mut hasher = DefaultHasher::new(); - ident.hash(&mut hasher); - let hashed_ident = hasher.finish(); BACKWARDS_MAP @@ -37,6 +37,10 @@ impl Identifier { Self { hashed_ident } } + + pub const fn new_unchecked(hashed_ident: u64) -> Self { + Self { hashed_ident } + } } impl OperatorIdentifier { @@ -67,95 +71,36 @@ impl Debug for OperatorIdentifier { } } -impl Identifier { - // builtin types - pub fn for_nah() -> Self { - Self::new("Nah") - } - - pub fn for_number() -> Self { - Self::new("Number") - } - - pub fn for_bool() -> Self { - Self::new("Bool") - } - - pub fn for_string() -> Self { - Self::new("String") - } - - pub fn for_function() -> Self { - Self::new("Function") - } - - pub fn for_type() -> Self { - Self::new("Type") - } - - pub fn for_native_object() -> Self { - Self::new("NativeObject") - } - - // builtin operators - pub fn for_plus() -> Self { - Self::new("+") - } - - pub fn for_minus() -> Self { - Self::new("-") - } - - pub fn for_multiply() -> Self { - Self::new("*") - } - - pub fn for_divide() -> Self { - Self::new("/") - } - - pub fn for_mod() -> Self { - Self::new("%") - } - - pub fn for_pow() -> Self { - Self::new("**") - } - - pub fn for_and() -> Self { - Self::new("&&") - } - - pub fn for_or() -> Self { - Self::new("||") - } - - pub fn for_combine() -> Self { - Self::new("<>") - } - - // builtin operators (comparison) - pub fn for_less() -> Self { - Self::new("<") - } - - pub fn for_less_eq() -> Self { - Self::new("<=") - } - - pub fn for_greater() -> Self { - Self::new(">") - } - - pub fn for_greater_eq() -> Self { - Self::new(">=") - } - - pub fn for_eq() -> Self { - Self::new("==") - } - - pub fn for_not_eq() -> Self { - Self::new("!=") - } +pub mod id { + use macros::static_ident; + + use crate::interpreter::identifier::Identifier; + + // types + pub const NAH: Identifier = static_ident!("Nah"); + pub const NUMBER: Identifier = static_ident!("Number"); + pub const BOOL: Identifier = static_ident!("Bool"); + pub const STRING: Identifier = static_ident!("String"); + pub const FUNCTION: Identifier = static_ident!("Function"); + pub const TYPE: Identifier = static_ident!("Type"); + pub const NATIVE_OBJECT: Identifier = static_ident!("NativeObject"); + + // arithmetic + pub const PLUS: Identifier = static_ident!("+"); + pub const MINUS: Identifier = static_ident!("-"); + pub const MULTIPLY: Identifier = static_ident!("*"); + pub const DIVIDE: Identifier = static_ident!("/"); + pub const MOD: Identifier = static_ident!("%"); + pub const POW: Identifier = static_ident!("**"); + pub const AND: Identifier = static_ident!("&&"); + pub const OR: Identifier = static_ident!("||"); + pub const COMBINE: Identifier = static_ident!("<>"); + + // comparison + pub const LESS: Identifier = static_ident!("<"); + pub const LESS_EQ: Identifier = static_ident!("<="); + pub const GREATER: Identifier = static_ident!(">"); + pub const GREATER_EQ: Identifier = static_ident!(">="); + pub const EQ: Identifier = static_ident!("=="); + pub const NOT_EQ: Identifier = static_ident!("!="); } diff --git a/src/interpreter/tree_sitter_parser.rs b/src/interpreter/tree_sitter_parser.rs index 9b05149..d669ea5 100644 --- a/src/interpreter/tree_sitter_parser.rs +++ b/src/interpreter/tree_sitter_parser.rs @@ -1,5 +1,6 @@ use std::{boxed::Box, collections::hash_map::Entry, collections::HashMap, rc::Rc, str::Utf8Error}; +use macros::static_ident; use snailquote::unescape; use thiserror::Error; use tree_sitter::{Node, Parser, Range}; @@ -12,11 +13,10 @@ use crate::interpreter::{ identifier::Identifier, statement::FruStatement, value::{ - fru_type::{Property, TypeType}, - fru_type::FruField, + fru_type::{FruField, Property, TypeType}, fru_value::FruValue, - function::{ArgumentList, FormalParameters} - } + function::{ArgumentList, FormalParameters}, + }, }; #[derive(Error, Debug)] @@ -559,7 +559,7 @@ fn parse_property(ast: NodeWrapper) -> Result { Item::Set( ( - ident.map_or_else(|| Identifier::new("value"), |x| x.0), + ident.map_or_else(|| static_ident!("value"), |x| x.0), x.parse_child_statement("body")?.wrap_rc(), ), x, diff --git a/src/interpreter/value/fru_value.rs b/src/interpreter/value/fru_value.rs index 2492213..2d5f8d2 100644 --- a/src/interpreter/value/fru_value.rs +++ b/src/interpreter/value/fru_value.rs @@ -2,11 +2,14 @@ use std::{fmt::Debug, rc::Rc}; use crate::interpreter::{ error::FruError, + identifier::id, identifier::Identifier, - value::fru_object::FruObject, - value::fru_type::FruType, - value::function::{AnyFunction, CurriedFunction, EvaluatedArgumentList, FruFunction}, - value::native::object::NativeObject, + value::{ + fru_object::FruObject, + fru_type::FruType, + function::{AnyFunction, CurriedFunction, EvaluatedArgumentList, FruFunction}, + native::object::NativeObject, + }, }; pub type TFnBuiltin = fn(EvaluatedArgumentList) -> Result; @@ -32,12 +35,12 @@ pub enum FruValue { impl FruValue { pub fn get_type_identifier(&self) -> Identifier { match self { - FruValue::Nah => Identifier::for_nah(), - FruValue::Number(_) => Identifier::for_number(), - FruValue::Bool(_) => Identifier::for_bool(), - FruValue::String(_) => Identifier::for_string(), - FruValue::Function(_) => Identifier::for_function(), - FruValue::Type(_) => Identifier::for_type(), + FruValue::Nah => id::NAH, + FruValue::Number(_) => id::NUMBER, + FruValue::Bool(_) => id::BOOL, + FruValue::String(_) => id::STRING, + FruValue::Function(_) => id::FUNCTION, + FruValue::Type(_) => id::TYPE, FruValue::Object(obj) => obj.get_type().get_ident(), FruValue::NativeObject(obj) => obj.get_type_identifier(), } diff --git a/src/interpreter/value/native/object.rs b/src/interpreter/value/native/object.rs index 5ff4559..fa83c67 100644 --- a/src/interpreter/value/native/object.rs +++ b/src/interpreter/value/native/object.rs @@ -1,16 +1,17 @@ -use std::any::Any; -use std::rc::Rc; +use std::{any::Any, rc::Rc}; use crate::interpreter::{ - error::FruError, identifier::Identifier, value::fru_value::FruValue, - value::function::EvaluatedArgumentList, + error::FruError, + identifier::id, + identifier::Identifier, + value::{fru_value::FruValue, function::EvaluatedArgumentList}, }; pub trait INativeObject { fn as_any(&self) -> &dyn Any; fn get_type_identifier(&self) -> Identifier { - Identifier::for_native_object() + id::NATIVE_OBJECT } fn call(&self, _args: EvaluatedArgumentList) -> Result { @@ -45,9 +46,7 @@ pub trait INativeObject { )) } - fn fru_clone(&self, value: &NativeObject) -> NativeObject { - value.clone() - } + fn fru_clone(self: Rc) -> Rc; } #[derive(Clone)] @@ -85,7 +84,9 @@ impl NativeObject { } pub fn fru_clone(&self) -> FruValue { - FruValue::NativeObject(self.internal.fru_clone(self)) + FruValue::NativeObject(NativeObject { + internal: self.internal.clone().fru_clone(), + }) } pub fn downcast(&self) -> Option<&T> { diff --git a/src/stdlib/scope/fru_scope.rs b/src/stdlib/scope/fru_scope.rs index 376739c..b809d51 100644 --- a/src/stdlib/scope/fru_scope.rs +++ b/src/stdlib/scope/fru_scope.rs @@ -1,5 +1,7 @@ use std::{any::Any, rc::Rc}; +use macros::static_ident; + use crate::interpreter::{ error::FruError, identifier::Identifier, @@ -24,7 +26,7 @@ impl INativeObject for FruScope { } fn get_type_identifier(&self) -> Identifier { - Identifier::new("Scope") + static_ident!("Scope") } fn get_prop(&self, ident: Identifier) -> Result { @@ -35,6 +37,10 @@ impl INativeObject for FruScope { self.scope.let_set_variable(ident, value); Ok(()) } + + fn fru_clone(self: Rc) -> Rc { + self + } } pub fn extract_scope_from_value(v: &FruValue) -> Option> { diff --git a/tests/oop/general_tests.rs b/tests/oop/general_tests.rs index 62911e7..f0599af 100644 --- a/tests/oop/general_tests.rs +++ b/tests/oop/general_tests.rs @@ -35,7 +35,7 @@ fn test_operators() { struct Vec2 { x; y; - } + } operator + (a : Vec2, b : Vec2) { Vec2 :{ a.x + b.x, a.y + b.y } diff --git a/tests/oop/property_tests.rs b/tests/oop/property_tests.rs index 85d3655..9ae1021 100644 --- a/tests/oop/property_tests.rs +++ b/tests/oop/property_tests.rs @@ -168,7 +168,7 @@ fn test_static_basics() { static Foo { get => inner + 5; set(val) { - inner = val - 5; + inner = val - 5; } } } From cf0568d2fd031b401f892d6109766c02e736905d Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Mon, 17 Jun 2024 13:12:54 +0300 Subject: [PATCH 5/6] feat: update highlighting --- docs/highlighter/highlighter-info.js | 40 +++++++++++------- docs/highlighter/tree-sitter-frugurt.wasm | Bin 49328 -> 56908 bytes docs/highlighter/tree-sitter.wasm | Bin 187641 -> 188635 bytes .../03-operators.md | 6 +-- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/docs/highlighter/highlighter-info.js b/docs/highlighter/highlighter-info.js index 2375719..aee1063 100644 --- a/docs/highlighter/highlighter-info.js +++ b/docs/highlighter/highlighter-info.js @@ -1,23 +1,30 @@ window.highlighterInfo = { - queryString: `"break" @keyword -"commutative" @keyword -"continue" @keyword -"else" @keyword -"fn" @keyword -"if" @keyword -"let" @keyword -"operator" @keyword -"pub" @keyword -"return" @keyword -"struct" @keyword -"static" @keyword -"while" @keyword -"impl" @keyword + queryString: `[ + "break" + "class" + "commutative" + "continue" + "data" + "else" + "fn" + "if" + "impl" + "import" + "let" + "operator" + "pub" + "return" + "scope" + "struct" + "static" + "while" +] @keyword (number_literal) @number (string_literal) @string -(bool_literal) @bool -(comment) @comment +(bool_literal) @bool +(nah_literal) @nah +(comment) @comment (let_statement ident: (identifier) @function.declaration @@ -33,6 +40,7 @@ window.highlighterInfo = { "number": "#2AACB8", "string": "#6AAB73", "bool": "#CF8E6D", + "nah": "#CF8E6D", "comment": "#7A7E85", }, }; diff --git a/docs/highlighter/tree-sitter-frugurt.wasm b/docs/highlighter/tree-sitter-frugurt.wasm index e557487c735f5066b03b06b1472669d8bdf21d4a..e27876a1765a314a6f9da1ae23ad78f76eda85fe 100644 GIT binary patch literal 56908 zcmeHw31Ae}{r`Krn{#)=AvXaw;SM))i3Bnt2{*_MBFiS(Kp+PsLB$KaZ^fgYSSea- zy=v9gqt>G0eH4`{-iVZ9i)2ApmU`B$=}sD{$EM zSH5C|hvDpX!I0>^2iU zuNZqxjh7T-pJZ%!RWbHU#{Sn7C<}n*PRxLzCGAsLtKP&Pi2jBB*UPRZD!9osD#-+ic(?3Ii=bT0cO;|a~!FB$jf z;vbNVziS%@CF2^M9a*#-mio zBx&U_$$COZ<_XEzsTog8#^u`V7RlJG8CxY|yH4`5WZa?|uS>=oIx5>F<73@)woAqb znz2JNw&^4vNyepCOOcGLHDkAAY|xCol5v4%?30WuG-JPHT%;KXB;!S$?Lo~TO{KG-KIB!5i-ZC zO;ULCd#aDzEg8?hs~Go6#y@lh4@kyCTH|5K*rqielZ@vy;|a;QLMM4zGCtRgEs}A) z_G7DL+@UpImW+2a<8{f{uNm7U;}f0jcFDL<8{Hup_aZjg$+8)LBw4rW=pL2db*Bl8mb~V}oRD(*5{G$@o$;Zjp=+HDjY>{HSegl8irU#@&+fwT{@mlJN(v z@qlDJsTmJT#wN{pOfoJ&Rb`u3sVBe+ndeX2&bLU_n{};jm5lo}<7LVCP&@RxWIV1J z+azPB&VRdPyrMOBNXBitqkSY9PwS4hQ!?(+jNOv)rDp7vjCV9+pJe=~83!a|i;ng| z$@pHktwUgh)Mexg(E=XSPF^G#2etNPlJSPNca>!9)Xi!G7$I}SzEKLF(%QF3#`ju# zqhvg!8Ji^IUCp>#G9J-3?v;!eG~)p<)adfC6#haBACruK=|=qoX`7d-r={>)o!J)2 z*sK{_CF2Fnc$t!$m$lcW@GBjiZIbbz?l9XW<3pW%2PIbnffU}XwRcL!lUjSXWc);y z#-B!aPMdI_<=;4fr?2zog;*aC%kmiBtP~?HD=ox`(V>->Wu+P{D+%xcUQb@F|JKzi zsXa}zk%YMM4l(Xv(RS;x(8GpB94_8mHQ>YUl7YqwBV zcK04Vd-d+qH>Y3!0RsmO8G8J%;Uh+lI$`vQV{*ri%QH?IfAWOf&F*-E3+j9^J%g&&Ocf?#oAnIGBd%(i?gGYfeNe8g_8 zv*UrN-`|~u_zL4WD8ZpD9#~9?Jt3d+b-$TBKR5@>oJrWhSD*6LD|`}e2A7#$g$ja2 za02dp$fo-8jN^q)c0Lq56M}`(&1j&IG(KYR*zh3Z5+eH)1eY4aKt=sVsz{cTA1sDo zP(d&ph>%K7K1`-SrI@He`N5g+nHWMAEDW6LH(Cg4V16)S48f@k*5H*!`i$Ud(n>ct zh6LpJjNszHLb5eR@T?yC%DbEC_Y z;5>tesJ4we*RS`jhR6OaOk}c>D0gnGU%&n`@9GJ`d4c(qcA!iq;(Iia#YToWr&3RX8nu&T z7+!2f<1?aX1Q#H9kP59}2$_2G&_K8C#N{nLlxFbLvy(#J71=3=e)BCwpajC)i&hEA8Gu6EcLPLsvfoFLS^g0N~9-TUbRap_-i=N)H|112F{^OSL8g@c_pdF)lDRHUTcvzIxZ`jE2_!+^TeuL7F#*m7fvi(MH z9J2gI9~?q{qc0BK$V6X9_6-_uMl_&R1c#gDLwg+(816T6;9wVWFsJbx9M%vAkGC8g z6gb{*^n-()$-#b&=itzWI5@;|a6n*)-{=nqJCcL_8_&VP4RLUg$T=^3^3 z=g_hOohKuoXEtxbYn9 z(+~%HTMl-oQFRC$Y(WkVX*>seHN?T5mV;RVF(#)3g5==P#&fVoLmcdGIoK`Gojg0< z>6y6h8aV~QfU%7_L3SJ+*PYp0Qm@`OKG_W^P?lAoF1X;KK*O8`lJ{&h2g8~}2nS5O zy9Cl>H6f~H+R{Sa*S4nH+WcTE=d`7@?WQ)^&Gd$*1)Xul#f&c{kV1J4cY4*3KHjw< z@#?)Q9XxJ9A47xoEKxLT#nm?1wuLcr<$Cv^#War-{^tv*4h+o)^pD!x~1cRmb9FL z8Iw7s9w}I`o=j4RXwuxm^o-`y1wlWCMguqVfeg&T<;2ZAij1yBZ3pXcek>U9)yNMw_yLbtu%eh8 zm&GY2Nwn~s+RDcQ%+LJ)%-~+VI_-tnT$S!*bipbV7OXwZW(m1*Ji)L*3(?s;qd3k| z^l@$4M%op|u0$7^OVNXmxE4KY_MEx%<}X;d=+wohEeS6zrlnplGQ~orhgLms&q2*D zKUe~0re5$6>t-GVoTKuvZst|X(j^8%O|}&LuaS(_bZI5h1GC0Hwlc#PEi7cmj#OE5PQtE}yX=+DqxN~@e51i1%f7@A)~ zbFtA8nlM63re1?Im(W6RXKnK|T2#ywY_tR9eyNs?#pQ0v$DYQIOA3 z5bvV-3mCQC1ITQik%i2Vo;sA*ppq}ZQXF+5kb6PS$7&b#Qjp}zJgiGmg9k~4pNlmp z0}IE(`j@conajZG4WmfbN9Y}Z-fS!tVM0d9{t0pxt(@ctl45oWR$nmLASo%&#F__g z?TMuF&A_UIF+fYFW2$cq6eRgKjV9EC1WEo)4NMZ<9BFrfoD!HwcTCFi1;+V}69q|yHLxZ|i+N_+Jem&Y3X+O54wGPGtRTtM z*uaT?W1PKJjy>^22HqC5XiAx$h*}mgXJF zrexVfw*nhC5*=~K^c&lylp@uQ=s!vNE6`o(a^3@KxM;p>sbv!Vo<(;KbnqKHtYn=6 z?dekE3$*nc?@Knh)Pd*^tYqy2ZRiP+50_9ppc0-_$=eeBk)$cSZE!V4`}EPp@ngxR z=HHs=mS6{IIPuYSvI7pdPJUu#kwNsQlBVJZ=>ob_(iHUoT`WJ7G)2)*SIyQilT4$c z57$a`MB&*tkfsv-xunTjO29{BsgJIoy>P(wGe}QSSjf2)i|eolS77)XLPO_t!{EVU zwVGKPjKttMF1r5xUwAkk5c}~iWhV17&XQOc?Ax-Q*!N_;vG2wDVn2%Y!=AGNKr$PG zeJVR1`*b!O`yd;MeG7I1_AS|o*tcT2*tcfmus@C&*tY?XvlT3g{Tx<~{VaA7_RH8k z*pFc!V1Ei*!#SJGvX}?RhG+D?V-MC1zI9+7StoWW@|w-&vUzMiJC!YFr?Dk0j8{EN z*m73NegSDN8;e}?kmpHkJUf|9VEL?oO=Oc;)0eO-%20a=-~2BqVaLOk@V)Y6Gpe?XO3|Brf_)#%k~oX7wb&Q2v#>8@??O8n z<5L=Ihko!j=4f8#!;CKn^S%C)t(zxyyP#wUO$*C+s+%`P6nd zTiYG=XGgK@v627VG0pwUW19QkqnNvFb*G6OkN#*Qb6;|da*u6wd+HJSWOJS#Z$6%8 z&c_3ov(lWrH|Fh4J1>_J4L6&J{-quft?$)Cmc8g0m0jk(^cdwH+m>mS=-Az_eR@oD z|Kl;u{gry;?lsq0cbjW?ZN)niyD)0d6UB5qPt3qO87;A<*Bb9&v@zeH$iy2S-NaiJ zJ@IbGD6IC$HN(+Jbt2n~l|Z>3Xdi#})M;HX$zkcQcz1)=5^J;A;s~pZYK)YADQo*+ zmDV6@vl={%)s(pYDT`mhTe-$ro9*p&ZkH{(5?eg1!c(Lb(@oP=x)j%#CJLN>mO zd-Xh6r>mzb%doh93{A+7QE_dINXW*RxHd*7WFs%Gjj;*YI61D3lM=EqF|LjLglz1N zTerIss@th?{g|APA2Z|Hn4XZ0IdN^wO322cxbgTQp?EBa>&Lu={8${<#-fC5ERAbp zNkTS?^Ns3we)u9&fGhF=OXz^!~e?sVPer2}}2COLV_l zKTFc5$AzWK96nuAN1v>FQG1D!c^h4#_;b+ON_D69#VS=@eNlHTv;vvpf}GuzYku#&+xIBM;hI@X$+ z^WO*?es(hE5%dNS`9iCWb-T7I8#f6Xtz9+}E6XjqELN@8wJfRyZgfQAwge+_5^9lF zKkRQq)os;7<^NOUZ+X9|j`^$9n;og|PB8U7j@0)inEF0P>IV``{g5N|!wIIQTC<{7 z*IH9id&H6Yu>|w}tt0gl38sF^k^1QbQ$OQKy(Pia&pA?WO)&L~j?^zFnEF*m>emxY z{iY-JwggkZ?MS^n!PM_MQtwDGHT7>AC*_qk(f&=n#7doLFZsaX`A2p1T#eA5m?LzO zZH%wm_^kTbPDlQ`>X^SuO)bVMdwp7rl{(QDv)keMo&-Io_GaasXnUg-No#C7h*fxX zr*OEtJQX!s)wfdbN-#C`am({W`#AL!D|Mnhg+?bUb)usaUCXT0iC)Y8>PWpe!TP2# z-O9T@W4e{Pu4B6DrTff#F?%nq+ntzdF*Hh8`PX%nP;1t5mHsPuk%KR1>263p3)qiu zQfTe}0FLx5;~a;&HgTRlI~HcWRlR;=oJSEHU{)wM+KJLTACTB_#h9?kM6 z(R(x+zpd1Xj^A|MvQpRex}}~II%5&r>fJGj|9ntA$Bb{yZR2pyFV*u^rzN+ABRyAD z{ayC-L+I~)6YlTdq35TFw^Qt`IcPpnvRnQE_LEKf8!)QmvRU9PuIJd&7{22VH`UR1 z4}Kd#`F=x#eGkNmjxQ#M`i;5qY$w}n%g8sL?Nr!KVd>bXCERxGE#^pm(|4S?^=vEg zYzJ&HX-GSaXIn-iLqx{eLle8w)NZ?Fli0Sm!TTBz@A~)6)*g=-OXA;8)Ewc zZT+rO@oKAolUNkQ?c*N zremMUz6N(COU4{%3hRpfXqJN2l&NeQu2u5+qD*N|GgDakM?F%`5-E?I=N#y9HeYBj z#J)3IjD7vQ4GSxOuhrXRvz!s=aaJs}OGIu5K$o*TL}eyoETd5>!^8uC*$K)@q?uQ>)e)kZ`tE=$?&zCX1J&J+E_x?jfXd=V;ejC$#=`1g-Oh7QGp& zY^gSNAtWAlv1nOocplqcm&=~Zr7`JMjO<#M*QzB;FUx(U(E1j>xZ`cNb+yp?&gOBv z9Iq8x-^bCi=XjmaiWhIY)(t|7qi&U7Dn>U8$xkqMr`DN?c**v0t1W6u_YR?ZyVh0w zp9%h*G5ntk{#`NrUkd&&V)zdV{{1ohUkm=PG+#Zbe-wNV`wjMf_P9B3P_ie%GzKY*LUey{B*{_}$WYz+S;!GA&XRUWT_-;T9JR4dsl$Ug;nDVfaw zO=z@d<6Zf`E$nU6_Ei4w3jTJ@ciP(Fu%%>jHR1!vg6u{_BbSZW<@`k0`zXeqoSE(v zX>NC=*)7uSib?YbqM6M0h&1OR&0_p6r1JZ(BF$c%M)AKA{CzR}uLXa94F4O!KcM-l zj0eF_X5V3-#tvcc*HQifR6EAJ6lGZt>TOSYC&gN6R~~twiPT;>FIH_m*-I-|^v1c$ zF%^2rEFF8iy&=<>Z6^a%JJt&O_Ke=wtpI;JVw1#HI&xH&juZNAg?=~DQj|olnY4$b zE6avtvCduAPe(}F3Q4Y(bQadS2x~HTRfcXcWylh`-G%NrN6tOLOJTi*Tt-d#B(ITu zA<1N8k!G~6<9^^Jv!999ve?DlWO9`+1B5U1jh%|S>LFg7>pT;I(Z?)tpLNZNAW;i6LfNIBPV{f%_8KwE) zrLei$OW6+RArAJIFS(aHxGK&I&7Po2cq%O7cWa?*)x6~8I(V{$9uHowj@fBQ+m?+2 zZ-M5?w96c6rQR5ap5&bPUCHgSrEhRrWhUSyB2KC@c57Kv~iK zxkL5~P*#4wbWrz$vT}IPLH!C;8}0S4L0N6$QHSPlKw0H`+@blTL-ThI>h})n4-V>C zP*yyjcTg`lG+%O1uQ;gJKv{8l!$JMgq4}1BdIyx1`=1=tdk)R_9n^=QtUCJGL469! zir{CCbf1H2gZ8Sn)i~J&%tj;L3Jmu!b`P)*80uy0OyC*7=VNR#umN}%$ixHLFz_?r zbD$?ayebCn0(JpyQy7~A+yJ}{v`S@cCU7nACJ;&d-_!Dpt=yV+N1(pEofz7~HU=NUrz>WeI0p|ce z1-1aY0AE|idI1xG6~I-%L%=rRYoHBohDHDjfU|&&z%#&3z|)?w9zZ^@9Jm5_0C)@d z2I$-YJ^>M61Mq8LJMazAp(A6XfyKbtz((MAzz4v;fexJ*8wtz<)&MsGj{Tn#({d=6yvV(bK< z7`PU=3)ljD415Q)@6FgSpa56|lmqL58-RO&CxI7%_kn%Dk3j1_=u^NbUfI^YiAm%!t|3&2~z$G{iBw?I-~^gW;p&<{8Pm;lTM!azB&7PuI=4!8rj4|p7S z4tN827x*055Bvv6%|ZVLvVlRsC}09G3kU-%fHlB*z~#VAz@5N-z~jJkz-z#}z-Pcd z;9J1ckFgd&CeRlc28;tH1G9j|Knbu4I19K4xCXcdxD&Vsco29D_#N;Z@CvXEcn|ml z_%pB%_$Tlk!26>-KntK9&;{rL^aF+hCjfas0Wcky3oHhTfEB=M;7nj0a0zfVa3gR# zuo?Iz@G$T=@C@(*@H+4g@B#1{uow6{@D1=i;2Xf0A7~A91VTV>U?4CY7z2z4CIhDc z3xF_C0>o}~-KA>`7k4&39Fst$0BJZ+2k43#1X|#{C64s_*mP~7Yk5bYGx(W67o2y) zF@$3_&;#fN-M%>X0|o$tKo13m<9sBJqj4Mqj77R}xceUuUOq4h^kf{TK{f+81(*%q z931C?zYw6i`Y=!kM3J@>D2Kcf$7=9S$9>5;D2dwW%p-yCP-s;I^^1QPVu+7BdX9JU zMJ(`kmb_P?YcHKs8R@(wj_J^+SR4nM;za4mc4whMHi#$Vnt~&SNyLxdPx1;r?jbqJ zN!A%W8FQbAC)uHxnze%aW;liaq?-y&vPI`X*f8z#Amn5-8FFP8mm;Bu%LD3Bo~NQ_ zRXXy!v(T5m%X;+Tu9;}6W3+eVL!RI=0Up`$;Hw@6aE`lcfC17sti|HZL75L7sa)hI z@yQ1|rz7zxj%1HwDchM(q^11qd6R#nOZn4%H`$=}M|P-Ih)?TU9<)%B`Jod)8QXwr zOWuO=LWgY9d@O((XoD!W6*UL}1r|GVljjuhM*Od`Fzd12mZcqGMYIRuxnkDFfs#w4 zlezfyAXV7xM`|zBp5;4asi=c=L_UC4=lrHg{!Wkl`y{kB*##>l-B^gpzhEN2-lFen z+`szZ{8imi`0FX7F(V#>xoT~H!DKR?8ql+X=@><3VicK$QDlz!*H9LSwL|;gM2Rr^ zM}Hxp1b=0P{yxeIJbNg^s8WGZ<$v4%BJjTm{4WCki@^WC5ukgU|BImepkwKGUGpcs z!$Fln**l!>(UvLS>_@ww#8*Bk9VJ(ELyrxyr_!opUHm28<_;=HqFY_k98TwOvikbs zM?z&+9f?O9%}JYLqkL3eWS{a=c@o{&n?0{1*;cytqdGrQTD!cqG{mn>rqb6%N9oe! zk7lbhKT<0!^os_xx{`!r8YHc83}R2KD=FgdH`4s56HwdyCd*3+f%%ewzwkH;Yb<%|SW! zCpZs+7eRhMgJcuZ{YL0t2aTn$aWhtlmVkE${`TcY_8`(M#$Uo*#9n5vuvdZC@$XIc zN4AZM@9%6s@HO@a*uU`3)wk>*`#1ZJ{f8Z5-?JaskEs4{arAN@PvXfug{Sf~p3eRF z+oM6A!CP>AVa!|e<9Hk1mbU{s@Q%C_@60oK7v7b3;~}2Kvw3&k182Q>Z{COZQC`BA@#Vaf zuiz_@svN18LSr?r;?=M<1@beX8{uccb}3Sv#n(Z%9JZsdxDaWl!s7U%>xt&A&OVNvohsR5{t)F@B z#fUTrU%oQ@J}7O&fst6PIT0hxIE+AzRX$-He+~B+wf3%36lZEzfpxOg z_*a9`|H#`}rb<(`PoKjLd;Oi;4D0WMgzFEFqHUYKeCwKF`4aDcYQ&U~(r z8nOPGAHvwb$l^6f~t{%RjTo8$HU zPYIXL|9`4|ex7joYOlXbn_>TJ%pnkf%>LIL?_UxxA70J0ZO#4q-kANbIgT%h*I(_| zhvs;FPrQ6`{YW)Yd;4sTl4kf|21^}Amgc)p5p4G-NohCrjK?Q zS0C*WeAR#P6^HZ}Jh`3YqN#rE`deIdv5UUhMPKQn?+|os{i9!yzos8n{GdsEkqW0~ zdlUU4E+73PE+73QE+73RE+73SE+73TE+73UE+73VE+730d{qye(>O}c)YNvZI8kzZ z)gWml#*<;%Ed7C!){IgYUE`wVyRw%3RW5m%i(ccRt6X%2i(V&bv8UZKNo)UBy6DX= z`hH1^J;mu12TcT-QvPeXwm(~(DLFl<6KS-(yNkw0KG-b%zLFMu@V$$1hF?Z8(`#DG zseJUNy<~8&Nt<$pr_Z$M?C}+alP+-4_=p6Xr9ah0(~}>&Ts{R-`rJqU;tLApkDK=7 zI?{7RD}4{OANuM+ZS;Hz`6;=V($m)`a^r5AyYv;U^gT;(hA%mkJw+=ypNlhe5hYi& zlKUb!!xtY)u4pCqklg04qLtihUME$0MJu_dP@HM|E*f9riIiq?j7vo>eUn!9l8CnX zt7t8!{Ood*R&pQF$Jxqj(n`+fh%@c4qLn;};$gRM(n>DkXSYvs&`Rzt!5O}&F&SLZ zT7D|d?ES-}mE5xwXZHFwX)O=C-SkA90^$xT|xlTU)6rzKak zmQ(%pu*pqY$pdu1G0KuFTFHYXKf#hKTFE`m`r?Y#@_eMXmEU91TJF3)a*s)C`8cFb zvhw$sw33VVNMGK`O}1By##f<2V6{J*7y2!IuSqL=UMf$(CU?`+9&G*1Ytn*CyHVmy z`GYS)U9|K1%$cNRHW z`5ot?f9}$M&P8u@(P=Kavx|P-MgP`C|I|f~booQiC+zV|cgfFi$y>VUOqc$HF8S{y zE%ua;v;An=l;bONSNba)=@}a!^V9khT=`$_N`IB0J*GbQ(EOnrfP9cr)sy1K>LEYq z#qyD_Ej`2guC!Tt<)8)tlN@^7Bl)pSi<9z2sR{ls3y17FsIvdiHg(K=Nxse~rJ09XE*G})v|QE|jCc*OB5Tg$od3$)Ic;xfpR zCpl^Sx`VW*65ySC+7!vXR4yw{Zd$z+Yw44Hn?Iu6Su}pxfy`U>g}*tLe~K1b&=c*# zx}IrT$whgFSm_n5<>dcho7_p`mtlma72hORJchdD*)Dp$i+0_Qd8wVDUzq{op6Q_b zY8t<^<4T|E%3t;yt6k9gQ6?}1m4f(ayOOVHx`(CBqAzyQOMt|Jbb`^esK%bu6g57qkIMbjLfHswE^I_VBBn&uI9{U2Sl*QGzeB@emi z<&qYAvRjU`-rAoS+C%Nf9$)u;Nx+r=DK2`Ui+)1pFSvC2kc1 zE_%I-KHo)O;G!>d(HFVsi(T|3F8WdzeVL1Xz*YWImpmlsbJ;oPu+e9;nzgH<;Z;?Y ztD;pkYgx_mXhpcNvUn|vmQ_dPS)^*&n(}BxO}MPIx`vgOud1x7VXG>uOKVCiDJT6$Icn3b ziq@>Ds<1JlXRfM>R#(F@8?&&ivS_89u8KzNbZLoASX5b2Q(CbmYGa8IStU4Qd1;wl zTvfVkc}=*qn95ofEm5bZN6KJDvZac!W7D(IVVmP?DoSgZNFmHo{BTmkmI2i@Rcngy z;mh(!4J#^(R9CZ-%Bpfy!m3CWqF)oOs%GWU@1g}^$3szWK5vf{hE0)=C>>N>< zk)k40n4Rrv09IsJ$(o8HYIJt((hAfJsx0Cb6s@U(14WUtGP}BL3pSgmd>bY6wlNR} z)a&wSb?I3szD-b4;p{qL3b$mTi^jh0p{(_|sF zO|~jhgWm-zK|IUL*P!c^o*reW`6vZgv`!{km*}LdB!aFgbEZmSq6}yvXr-*Wa!plH zG+aXGr$?$vBZW8-?M^lc7Og0jG&);ZG1?b0D`KnG6tdz-O@!r+Wn;#&vAHZamyO9~ zS=eLUZM1Gf;hM^o(Tbtcgc?P{Rnb+^NX-z*)g4E(W16g%$R2_4X^t9P5TNQBS)0nb zt!!FdwQcFw5X`RzV(p_p)+2IqaC6_I59`&-ya5-ZBx6~)cgV(b!X8*F?1?Xwdtr^c z57P8S+8lhL)(>AX_ZLiJV4sICkbB{G;JV-|W&@7}EkOTAs=&qG8$ONtC5@^+S9I(X1IXDv3MzK@z zqBsr2Gqypv#~zI}2#QfIB1y5Kxb(yG)&b(HRF#@Rj%J0(^aOeVIY2*P05Av`1B?a6 z0ZV{$0Q;7UTpE^sJnYXtqK(?~pK!#qhx5M>?V|{*b;Wphf_`7H1nqDc;#Z1ylFG2A zU4eL2qV=r8T7DJgUe#zzHF!>chWMVBp4j8JD9wP#l@z>zg)4kV@Mz_z3lIcmuE7r5 zPAoGIz7oF&iRJ87Sen|h6z35fi?BK!#qVnZaDwe$N!oI91apVo23J|)Fs2jN;1=XY z90y{_`X(Hox)1FKSO5$KeLav2oojH+gU)vk!cUx!0`39-@1Qeq{ymPfKu?Fx$-opK z19T@~E#zx}3LqWyc{qA-d=zP(0Coat;AI2baefOh4e3vV4~u}AIKLFKtAP!`1;7=+ zML<4eCjpayvA}F#B5*!r8<2J^ju#*=^6lpP@jIBnKfoV|<2D?h1JZF`1Y8ev0cHcM zfKMR15XU~SaVydn;kXpM+u`3sz--W0;kXAndvJUSm<(P8jsYNqH08jlz&g+~fUj{r z4#yKn4qO0x3OD2Ye&9psJPs^{{1qHKp*&B+?mfVlz&pT?0Bw1YpNljU{|9k?CeEkf zxD!56d`IK_dz?Q6ybBBkJprJ2^alO~9jcpeA=?bR08l+uz|H_1KLkzXZx1|)^R`Iy F{{R@p>$Cs> literal 49328 zcmeHQ34Bz={h!(0ocorW0O8s!R|xlgACDv94hnJ!hMW)x2_ylwy;Wt_OJ5Dk>@{S}gzXZ|2RsBb%^GNc{Kz>4xun?{~~^e)F5-&CGkd zu>7i3oZ-_vxoCY^Y5B^&{dwZccl!c9#ycb~mNeSs<&~Bfl~xuPR_B$hEibGt zttgji_-f|!Cnj*7;PWMA`#}(s{WbD_BBa*S_1*LJ67+m_a5&m`IrV`yu zBr^}bAsAsQ_P%Y(-Fv0*)vb!LLo&A9tQb2br>;|s z-IB3OGxkWvueFVrCF6w~Rg%{w<5I2hmSscxv0pMa>au(w8QV1DW69W|83!ceb6whl zlJWiTRJMmCV~b95L^6H`M#LxMdsMQnffdyZHvUn>_yz6UCdqh0Gd4@cbDD9nWW1_N ze3@jtrx{x$<2&ukRg!U-eDOL$DejIcEtT_uJ4Zcxqn zI?1?5Gqy^`H@dIhEE)G`#&*frrWtog#$L_1TQY9fM(>r3ziY-0$@oww*(n*iz zu|qSSkc>xklBXr(IjylkGJda{;C{(? zKr=p&jF+^Hk0s+ut#Lpy?$rI~pk#chH4aI}Ud=cn8P95?M+O!~JEZCn9o)Mm<6>Qj_e#d)+WHR3IHt?6Q!%Ov9lt+7QizSoSaB;yio^g1xq$gouk zztGw@lhC?}Y?s1EwD1ne_(bP^w`5$RHSVS4)(vNe6uzp1u~Rbsq#OG#$=IfoKS9Z@ zPW-eKUaz%xOU8LxdyiziEg8WSn!;?wgeQ1iAD+6wKR(PdeJsOg`ZJQul#G-xBSzci zewLAJvWx^E0Qi05YyI0$tEBcc|7Rp&ZXP@4U(;kRY)s zoK|yNr?qL@F1>w+j-5J(Ga_BOcI)0Fvu9SX-hKM^8!≪2}eY4IeRb)aWr|$Bj29 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 diff --git a/docs/highlighter/tree-sitter.wasm b/docs/highlighter/tree-sitter.wasm index 94bdca9f35c72d6486bc0421fae52688ba6883e4..0ac880b80e5eb85299e9eed43124a465a6f52ee0 100644 GIT binary patch delta 8408 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 RLuF(IqgsCN?PArOe*h%Npk)96 delta 7418 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 N%+I~z9(MHP{{p9O0c-#O diff --git a/docs/src/03-object-oriented-programming/03-operators.md b/docs/src/03-object-oriented-programming/03-operators.md index a9cfd97..507a17a 100644 --- a/docs/src/03-object-oriented-programming/03-operators.md +++ b/docs/src/03-object-oriented-programming/03-operators.md @@ -40,13 +40,13 @@ print(a); // Vector{x=4, y=6} Operator precedences from highest to lowest: -- Custom operators have the highest precedence +- All custom operators - `**` `<>` - `*` `/` `%` - `+` `-` - `<` `>` `<=` `>=` - `==` `!=` - `&&` -- `||` - the lowest precedence +- `||` -All operators have left associativity \ No newline at end of file +All operators are left associative From 1a05e53b96371ea263d12a2c6d841297aa10de13 Mon Sep 17 00:00:00 2001 From: BadCatSet Date: Mon, 17 Jun 2024 13:43:06 +0300 Subject: [PATCH 6/6] docs: write chapters for import and scope manipulation --- Cargo.toml | 6 ++-- docs/src/04-scope-manipulation/01-index.md | 3 ++ docs/src/04-scope-manipulation/02-scope.md | 35 ++++++++++++++++++++ docs/src/04-scope-manipulation/03-imports.md | 29 ++++++++++++++++ docs/src/SUMMARY.md | 3 ++ 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 docs/src/04-scope-manipulation/01-index.md create mode 100644 docs/src/04-scope-manipulation/02-scope.md create mode 100644 docs/src/04-scope-manipulation/03-imports.md diff --git a/Cargo.toml b/Cargo.toml index c6cba38..8c95443 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ serde_json = "1.0.115" snailquote = "0.3.1" thiserror = "1.0.58" tree-sitter = "0.22.5" -#tree-sitter-frugurt = "0.0.10" -tree-sitter-frugurt = { path = "../tree-sitter-frugurt" } +tree-sitter-frugurt = "0.0.11" +#tree-sitter-frugurt = { path = "../tree-sitter-frugurt" } #uid = "0.1.7" -macros = {path = "./macros"} +macros = { path = "./macros" } ctor = "0.2.8" [dev-dependencies] diff --git a/docs/src/04-scope-manipulation/01-index.md b/docs/src/04-scope-manipulation/01-index.md new file mode 100644 index 0000000..9f0717a --- /dev/null +++ b/docs/src/04-scope-manipulation/01-index.md @@ -0,0 +1,3 @@ +# Scope manipulation + +Frugurt supports explicit scope capturing and subsequent manipulation. diff --git a/docs/src/04-scope-manipulation/02-scope.md b/docs/src/04-scope-manipulation/02-scope.md new file mode 100644 index 0000000..ceb66e0 --- /dev/null +++ b/docs/src/04-scope-manipulation/02-scope.md @@ -0,0 +1,35 @@ +# Scope keyword + +Scope keyword can be used in three constructs: + +- `scope()` - captures scope in which it was evaluated +- `scope s { statements... }` - run statements in specified scope +- `scope s { statements... expression }` - run statements in specified scope and return result of expression + +Example: + +```frugurt +let f = fn () { + let a = 5; + let b = 3; + scope() +}; + +let scope_object = f(); + +print(scope_object.a); // 5 + +scope scope_object { + // this statement is executed in the same scope as the body of function f ran + // so the variables a and b are available here + print(a * b); // 15 +} + +scope_object.a = 10; // old variables can be re-assigned +scope_object.c = 20; // new variables can be declared + +print(scope scope_object { + let r = a + c; + r * b +}); // 90 +``` diff --git a/docs/src/04-scope-manipulation/03-imports.md b/docs/src/04-scope-manipulation/03-imports.md new file mode 100644 index 0000000..465ff1f --- /dev/null +++ b/docs/src/04-scope-manipulation/03-imports.md @@ -0,0 +1,29 @@ +# Imports + +Other files can be imported into your code by using the `import` expression. +Import expression returns the same scope object, which was mentioned in the previous chapter. + +Example: + +`main.fru` +```frugurt +let foo = import "foo.fru"; + +print(foo.f(1, 2)); // 3 + +// this is as badass as extremely stupid +scope foo { + let wow = 5; + + print(omg()); // 5 +} +``` + +`foo.fru` +```frugurt +let f = fn(x, y) { + x + y +}; + +let omg = fn() { wow }; +``` diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 73fd5a4..bac3f87 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -17,3 +17,6 @@ - [Methods](./03-object-oriented-programming/04-methods.md) - [Statics](./03-object-oriented-programming/05-statics.md) - [Properties](./03-object-oriented-programming/06-properties.md) +- [Scope manipulation](./04-scope-manipulation/01-index.md) + - [Scope keyword](./04-scope-manipulation/02-scope.md) + - [Imports](./04-scope-manipulation/03-imports.md) \ No newline at end of file