commit 5c1cff92c60064636a0a027b3a95fe757a6d8171 Author: mb Date: Mon Aug 29 16:52:55 2022 +0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8eed9e8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["lorgn_lang", "lorgn_runtime"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..eda7bc9 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# LORGN + +Logic Organizer for Redaction in Graphical Notations + +--- + +## Description + +LORGN is a general purpose scripting language optimized for graphical programming. \ No newline at end of file diff --git a/lorgn_lang/Cargo.toml b/lorgn_lang/Cargo.toml new file mode 100644 index 0000000..1a3a69d --- /dev/null +++ b/lorgn_lang/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "lorgn_lang" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive"]} \ No newline at end of file diff --git a/lorgn_lang/src/ast.rs b/lorgn_lang/src/ast.rs new file mode 100644 index 0000000..a22680b --- /dev/null +++ b/lorgn_lang/src/ast.rs @@ -0,0 +1,74 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct Path { + pub module: Name, + pub item: Name, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct Name(pub String); + +impl From for Name { + fn from(input: String) -> Self { + Self(input) + } +} + +impl From for String { + fn from(input: Name) -> Self { + let Name(result) = input; + result + } +} + +impl<'s> From<&'s str> for Name { + fn from(input: &'s str) -> Self { + input.to_string().into() + } +} + +pub struct Import { + pub module_name: Name, + pub items: Vec, +} +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Export { + pub items: Vec, +} + +pub use expression::*; +mod expression; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FnDef { + pub name: Name, + pub parameters: Vec, + pub expressions: Block, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum TopLevel { + Export(Export), + FnDef(FnDef), +} + +impl TopLevel { + pub fn as_fndef(&self) -> Option<&FnDef> { + match self { + Self::FnDef(result) => Some(result), + _ => None, + } + } + pub fn into_fndef(self) -> Option { + match self { + Self::FnDef(result) => Some(result), + _ => None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Module { + pub items: Vec, +} diff --git a/lorgn_lang/src/ast/expression.rs b/lorgn_lang/src/ast/expression.rs new file mode 100644 index 0000000..924ce10 --- /dev/null +++ b/lorgn_lang/src/ast/expression.rs @@ -0,0 +1,135 @@ +use serde::{Deserialize, Serialize}; + +use super::{Name, Path}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Block { + pub expressions: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Assignment { + pub variable_name: Name, + pub value: BExpr, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Invoke { + pub variable_name: Name, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Litteral { + String(String), + Integer(i32), + Float(f32), + Bool(bool), + List(Vec), + Map(Vec<(Name, BExpr)>), +} + +impl From for Litteral { + fn from(input: String) -> Self { + Self::String(input) + } +} + +impl From<&str> for Litteral { + fn from(input: &str) -> Self { + Self::String(input.to_string()) + } +} + +impl From for Litteral { + fn from(input: i32) -> Self { + Self::Integer(input) + } +} + +impl From for Litteral { + fn from(input: f32) -> Self { + Self::Float(input) + } +} + +impl From for Litteral { + fn from(bool: bool) -> Self { + Self::Bool(bool) + } +} + +impl From> for Litteral +where + T: Into, +{ + fn from(input: Vec) -> Self { + let list = input + .into_iter() + .map(|e| Box::new(Expr::Litteral(e.into()))) + .collect(); + Self::List(list) + } +} + +impl From> for Litteral +where + S: ToString, + T: Into, +{ + fn from(input: Vec<(S, T)>) -> Self { + let list = input + .into_iter() + .map(|(n, e)| (n.to_string().into(), Box::new(Expr::Litteral(e.into())))) + .collect(); + Self::Map(list) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FnCall { + pub fn_path: Path, + pub arguments: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Condition { + pub condition: BExpr, + pub true_case: BExpr, + pub false_case: BExpr, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Loop { + pub body: BExpr, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Return { + pub expression: BExpr, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Break { + pub expression: BExpr, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Expr { + Block(Block), + Assignment(Assignment), + Invoke(Invoke), + Litteral(Litteral), + FnCall(FnCall), + Condition(Condition), + Loop(Loop), + Return(Return), + Break(Break), +} + +impl Expr { + pub fn boxed(self) -> BExpr { + Box::new(self) + } +} + +pub type BExpr = Box; diff --git a/lorgn_lang/src/lib.rs b/lorgn_lang/src/lib.rs new file mode 100644 index 0000000..d0cd2ba --- /dev/null +++ b/lorgn_lang/src/lib.rs @@ -0,0 +1,2 @@ +pub mod ast; +pub mod typing; diff --git a/lorgn_lang/src/typing.rs b/lorgn_lang/src/typing.rs new file mode 100644 index 0000000..947f68f --- /dev/null +++ b/lorgn_lang/src/typing.rs @@ -0,0 +1,6 @@ +pub enum Primitive { + String(String), + Integer(i32), + Float(f32), + Bool(bool), +} diff --git a/lorgn_runtime/Cargo.toml b/lorgn_runtime/Cargo.toml new file mode 100644 index 0000000..1fe6ece --- /dev/null +++ b/lorgn_runtime/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lorgn_runtime" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lorgn_lang = { path = "../lorgn_lang" } +gc = "0.4" +gc_derive = "0.4" +ron = "0.8" diff --git a/lorgn_runtime/src/function.rs b/lorgn_runtime/src/function.rs new file mode 100644 index 0000000..843c3c4 --- /dev/null +++ b/lorgn_runtime/src/function.rs @@ -0,0 +1,91 @@ +use std::fmt::Debug; + +use lorgn_lang::ast::{FnDef, Name, Path}; + +use crate::{runtime::Context, Value}; + +#[derive(Debug)] +pub struct Imported { + _path: Path, +} + +pub struct Native { + arg_count: usize, + handler: Box) -> Value>, +} + +impl Native { + pub fn run(&mut self, args: Vec) -> Value { + if self.arg_count != args.len() { + panic!("too few args") + } + (self.handler)(args) + } +} + +impl Debug for Native { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("").finish() + } +} + +#[derive(Debug)] +pub enum FnImpl { + Imported(Imported), + Defined(FnDef), + Native(Native), +} + +#[derive(Debug)] +pub struct Function { + name: Name, + implem: FnImpl, +} + +impl Function { + pub fn new_defined(name: Name, definition: FnDef) -> Self { + let implem = FnImpl::Defined(definition); + Self { name, implem } + } + + pub fn new_imported(name: Name, module: Name) -> Self { + let implem = FnImpl::Imported(Imported { + _path: Path { + item: name.clone(), + module, + }, + }); + Self { name, implem } + } + pub fn new_native( + name: Name, + mut caller: impl FnMut([Value; N]) -> Value + 'static, + ) -> Self { + let handler = Box::new(move |values: Vec| { + let casted = values.try_into().unwrap(); + (caller)(casted) + }) as Box) -> Value>; + Self { + name, + implem: FnImpl::Native(Native { + arg_count: N, + handler, + }), + } + } + + pub fn call(&mut self, args: Vec, context: &mut Context) -> Value { + match &mut self.implem { + FnImpl::Defined(definition) => context.run_fun(definition, args), + FnImpl::Native(native) => native.run(args), + FnImpl::Imported(_imported) => { + todo!() // let mut res = context.find_function(&imported.path).unwrap(); + // res.call(args, context) + } + } + } + + pub fn name(&self) -> &Name { + &self.name + } +} diff --git a/lorgn_runtime/src/lib.rs b/lorgn_runtime/src/lib.rs new file mode 100644 index 0000000..9637a21 --- /dev/null +++ b/lorgn_runtime/src/lib.rs @@ -0,0 +1,31 @@ +mod module; +pub use module::Module; + +mod function; +pub use function::Function; + +mod runtime; +pub use runtime::Runtime; + +mod value; +pub use value::Value; + +#[test] +fn test_runtime() { + use lorgn_lang::ast::{Expr, FnCall, Path}; + + let mut runtime = Runtime::default(); + let mut module = Module::new_empty("std"); + module.push_native("print".into(), |[item]: [Value; 1]| { + println!("{item:?}"); + Value::None + }); + runtime.register(module); + runtime.evaluate(Expr::FnCall(FnCall { + fn_path: Path { + module: "std".into(), + item: "print".into(), + }, + arguments: vec![Expr::Litteral("hello yorld".into()).boxed()], + })); +} diff --git a/lorgn_runtime/src/module.rs b/lorgn_runtime/src/module.rs new file mode 100644 index 0000000..421a781 --- /dev/null +++ b/lorgn_runtime/src/module.rs @@ -0,0 +1,66 @@ +use std::{ + cell::{RefCell, RefMut}, + collections::{HashMap, HashSet}, +}; + +use lorgn_lang::ast::{self, Name, TopLevel}; + +use crate::{Function, Value}; + +#[derive(Debug)] +pub struct Module { + name: Name, + functions: HashMap>, + _exports: HashSet, // TODO +} + +impl Module { + pub fn new_empty(name: impl ToString) -> Self { + let name = name.to_string().into(); + Self { + _exports: HashSet::new(), + functions: HashMap::new(), + name, + } + } + pub fn from_ast(name: impl ToString, content: ast::Module) -> Self { + let name = name.to_string().into(); + let mut functions = HashMap::new(); + let mut exports = HashSet::new(); + + for item in content.items { + match item { + TopLevel::Export(export) => export.items.iter().for_each(|e| { + exports.insert(e.clone()); + }), + TopLevel::FnDef(fndef) => { + let name = fndef.name.clone(); + let fun = Function::new_defined(name.clone(), fndef); + functions.insert(name, RefCell::new(fun)); + } + }; + } + + Self { + name, + functions, + _exports: exports, + } + } + pub fn push_native( + &mut self, + name: Name, + caller: impl FnMut([Value; N]) -> Value + 'static, + ) { + let nat = Function::new_native(name.clone(), caller); + self.functions.insert(name, RefCell::new(nat)); + } + + pub fn name(&self) -> &Name { + &self.name + } + + pub fn get_function(&self, name: &Name) -> Option> { + self.functions.get(name).map(|entry| entry.borrow_mut()) + } +} diff --git a/lorgn_runtime/src/runtime.rs b/lorgn_runtime/src/runtime.rs new file mode 100644 index 0000000..2b613e3 --- /dev/null +++ b/lorgn_runtime/src/runtime.rs @@ -0,0 +1,287 @@ +use std::collections::HashMap; + +use lorgn_lang::ast::{Expr, Name}; + +use crate::{Module, Value}; + +pub struct Runtime { + modules: HashMap, +} + +impl Default for Runtime { + fn default() -> Self { + let modules = HashMap::new(); + Self { modules } + } +} + +impl Runtime { + pub fn register(&mut self, module: Module) { + self.modules.insert(module.name().clone(), module); + } + + pub fn evaluate(&mut self, expression: Expr) -> Value { + let mut context = self.context(); + context.run_expr(&expression) + } + + fn context(&mut self) -> Context { + Context::new(&mut self.modules) + } +} + +pub use eval_result::EvRes; +mod eval_result; + +pub struct Scope { + bubble_variables: bool, + variables: HashMap, +} + +impl Scope { + pub fn new(bubble_variables: bool) -> Self { + let variables = HashMap::new(); + Self { + bubble_variables, + variables, + } + } + + pub fn new_with(variables: Vec<(Name, Value)>, bubble_variables: bool) -> Self { + let mut result = Self::new(bubble_variables); + for (name, value) in variables { + result.insert(name, value) + } + result + } + + pub fn insert(&mut self, name: Name, value: Value) { + self.variables.insert(name, value); + } + + pub fn get(&self, name: &Name) -> Option<&Value> { + self.variables.get(name) + } + + pub fn get_mut(&mut self, name: &Name) -> Option<&mut Value> { + self.variables.get_mut(name) + } +} + +pub use context::Context; +mod context { + use std::{cell::RefMut, collections::HashMap}; + + use lorgn_lang::ast::{ + Assignment, Block, Break, Condition, Expr, FnCall, FnDef, Invoke, Litteral, Loop, Name, + Path, Return, + }; + + use crate::{Function, Module, Value}; + + use super::{EvRes, Scope}; + + pub struct Context<'r> { + modules: &'r HashMap, + scopes: Vec, + } + + impl<'r> Context<'r> { + pub fn new(modules: &'r mut HashMap) -> Self { + let scopes = vec![]; + Self { modules, scopes } + } + + pub fn find_function(&self, path: &Path) -> Option> { + self.modules + .get(&path.module) + .and_then(|m| m.get_function(&path.item)) + } + + pub fn find_variable(&mut self, name: &Name) -> Option<&mut Value> { + for scope in self.scopes.iter_mut().rev() { + if !scope.bubble_variables { + return None; + } + if let Some(result) = scope.variables.get_mut(name) { + return Some(result); + } + } + None + } + + pub fn run_fun(&mut self, fn_def: &FnDef, params: Vec) -> Value { + let variables = fn_def + .parameters + .iter() + .cloned() + .zip(params.into_iter()) + .collect(); + self.push_scope(Scope::new_with(variables, false)); + let res = self.eval_block(&fn_def.expressions); + self.pop_scope(); + match res { + EvRes::Value(res) => res, + EvRes::ReturnSC(res) => res, + EvRes::BreakSC(_) => panic!("break outside of loop"), + } + } + + pub fn run_expr(&mut self, expr: &Expr) -> Value { + self.eval_expr(expr).into_value().unwrap() + } + + fn push_scope(&mut self, scope: Scope) { + self.scopes.push(scope) + } + + fn pop_scope(&mut self) { + self.scopes.pop(); + } + + pub fn top_scope(&mut self) -> Option<&mut Scope> { + self.scopes.last_mut() + } + + pub fn top_index(&self) -> usize { + self.scopes.len() - 1 + } + + fn eval_expr(&mut self, expr: &Expr) -> EvRes { + match expr { + Expr::Block(block) => self.eval_block(block), + Expr::Assignment(assignment) => self.eval_assignment(assignment), + Expr::Invoke(invoke) => self.eval_invoke(invoke), + Expr::Litteral(litteral) => self.eval_litteral(litteral), + Expr::FnCall(fn_call) => self.eval_fn_call(fn_call), + Expr::Condition(condition) => self.eval_condition(condition), + Expr::Loop(loop_) => self.eval_loop(loop_), + Expr::Return(return_) => self.eval_return(return_), + Expr::Break(break_) => self.eval_break(break_), + } + } + + fn eval_block(&mut self, block: &Block) -> EvRes { + let mut last = None; + for expr in &block.expressions { + let result = self.eval_expr(expr); + if let EvRes::Value(result) = result { + last = Some(result); + } else { + return result; + } + } + let result = last.unwrap_or(Value::None); + EvRes::new_val(result) + } + + fn eval_assignment(&mut self, assignment: &Assignment) -> EvRes { + let result = self.eval_expr(&assignment.value); + if let EvRes::Value(result) = result { + let name = assignment.variable_name.clone(); + self.top_scope().unwrap().insert(name, result.clone()); + EvRes::new_val(result) + } else { + result + } + } + + fn eval_invoke(&mut self, invoke: &Invoke) -> EvRes { + let value = self.find_variable(&invoke.variable_name).unwrap().clone(); + EvRes::new_val(value) + } + + fn eval_litteral(&mut self, litteral: &Litteral) -> EvRes { + match litteral { + Litteral::String(str) => EvRes::Value(str.clone().into()), + Litteral::Integer(int) => EvRes::Value((*int).into()), + Litteral::Float(flt) => EvRes::Value((*flt).into()), + Litteral::Bool(bool) => EvRes::Value((*bool).into()), + Litteral::List(vec) => { + let mut results = vec![]; + for expr in vec { + let result = self.eval_expr(expr); + if let EvRes::Value(result) = result { + results.push(result); + } else { + return result; + } + } + EvRes::Value(results.into()) + } + Litteral::Map(map) => { + let mut results = HashMap::new(); + for (name, expr) in map { + let result = self.eval_expr(expr); + if let EvRes::Value(result) = result { + results.insert(name.clone(), result); + } else { + return result; + } + } + EvRes::Value(results.into()) + } + } + } + + fn eval_fn_call(&mut self, fn_call: &FnCall) -> EvRes { + let mut args = vec![]; + for arg in &fn_call.arguments { + let res = self.eval_expr(arg); + if res.is_short_circuit() { + return res; + } + args.push(res.into_value().unwrap()); + } + let path = &fn_call.fn_path; + let module = self.modules.get(&path.module).unwrap(); + let mut function = module.get_function(&path.item).unwrap(); + let res = function.call(args, self); + EvRes::Value(res) + } + + fn eval_condition(&mut self, condition: &Condition) -> EvRes { + let cond = self.eval_expr(&condition.condition); + if cond.is_short_circuit() { + return cond; + } + let cond = cond.into_value().unwrap(); + if cond.into_bool().unwrap() { + self.eval_expr(&condition.true_case) + } else { + self.eval_expr(&condition.false_case) + } + } + + fn eval_loop(&mut self, loop_: &Loop) -> EvRes { + let body = &loop_.body; + let result = loop { + let res = self.eval_expr(body); + match res { + EvRes::ReturnSC(_) => return res, + EvRes::BreakSC(result) => break result, + _ => (), + }; + }; + EvRes::new_val(result) + } + + fn eval_return(&mut self, return_: &Return) -> EvRes { + let result = self.eval_expr(&return_.expression); + match result { + EvRes::ReturnSC(_) => result, + EvRes::Value(v) => EvRes::ReturnSC(v), + EvRes::BreakSC(_) => panic!("break outside of loop"), + } + } + + fn eval_break(&mut self, break_: &Break) -> EvRes { + let result = self.eval_expr(&break_.expression); + match result { + EvRes::ReturnSC(_) => result, + EvRes::Value(v) => EvRes::BreakSC(v), + EvRes::BreakSC(v) => EvRes::BreakSC(v), + } + } + } +} diff --git a/lorgn_runtime/src/runtime/eval_result.rs b/lorgn_runtime/src/runtime/eval_result.rs new file mode 100644 index 0000000..99627af --- /dev/null +++ b/lorgn_runtime/src/runtime/eval_result.rs @@ -0,0 +1,28 @@ +use crate::Value; + +// TODO: refactor into {res} | {sc {} | {}} +pub enum EvRes { + Value(Value), + ReturnSC(Value), + BreakSC(Value), +} + +impl EvRes { + pub fn new_val(value: Value) -> Self { + Self::Value(value) + } + + pub fn is_short_circuit(&self) -> bool { + match self { + EvRes::Value(_) => false, + _ => true, + } + } + + pub fn into_value(self) -> Option { + match self { + Self::Value(val) => Some(val), + _ => None, + } + } +} diff --git a/lorgn_runtime/src/value.rs b/lorgn_runtime/src/value.rs new file mode 100644 index 0000000..60ce5a1 --- /dev/null +++ b/lorgn_runtime/src/value.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; + +use gc::Gc; +use gc_derive::{Finalize, Trace}; +use lorgn_lang::ast::Name; + +#[derive(Debug, Clone, Trace, Finalize)] +pub struct InnerObj(#[unsafe_ignore_trace] HashMap); + +#[derive(Debug, Clone, Trace, Finalize)] +pub enum Value { + String(String), + Integer(i32), + Float(f32), + Bool(bool), + List(Gc>), + Object(Gc), + None, +} + +impl Value { + pub fn into_string(self) -> Option { + match &self { + Self::String(str) => Some(str.clone()), + _ => None, + } + } + pub fn into_i32(self) -> Option { + match self { + Self::Integer(int) => Some(int), + _ => None, + } + } + pub fn into_bool(self) -> Option { + match self { + Self::Bool(bool) => Some(bool), + _ => None, + } + } +} + +impl From for Value { + fn from(input: String) -> Self { + Self::String(input) + } +} + +impl From for Value { + fn from(input: i32) -> Self { + Self::Integer(input) + } +} + +impl From for Value { + fn from(input: f32) -> Self { + Self::Float(input) + } +} + +impl From for Value { + fn from(input: bool) -> Self { + Self::Bool(input) + } +} + +impl From> for Value { + fn from(input: Vec) -> Self { + let gc = Gc::new(input); + Self::List(gc) + } +} + +impl From> for Value { + fn from(input: HashMap) -> Self { + let gc = Gc::new(InnerObj(input)); + Self::Object(gc) + } +} + +impl From> for Value +where + T: Into, +{ + fn from(input: Option) -> Self { + match input { + Some(input) => input.into(), + None => Self::None, + } + } +}