diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..9fd45e0 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dabe9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +publish.sh +Cargo.lock +target +scripts diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4f16a73 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "bellronos" +version = "0.0.1" +edition = "2021" +authors = ["Ronaldson Bellande "] +description = "Bellande Operating System Programming Language written in Rust" +license = "GPL-3.0-or-later" +repository = "https://github.com/Architecture-Mechanism/bellronos" +documentation = "https://bellande-architecture-mechanism-research-innovation-center.org/bellronos/docs" +readme = "README.md" +keywords = ["programming-language", "operating-system", "bellronos"] +categories = ["compilers", "development-tools"] + +[lib] +name = "bellronos" +path = "src/bellronos.rs" + +[dependencies] +glob = "0.3.1" +tempfile = "3.3.0" +shellexpand = "3.1.0" +meval = "0.2.0" +reqwest = { version = "0.11", features = ["json", "blocking"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +anyhow = "1.0" +clap = { version = "4.0", features = ["derive"] } +regex = "1.7" +lazy_static = "1.4" +log = "0.4" +env_logger = "0.10" +tokio = { version = "1.0", features = ["full"] } +async-trait = "0.1" +futures = "0.3" +chrono = "0.4" +rand = "0.8" + +[dev-dependencies] +assert_cmd = "2.0" +predicates = "3.0" +criterion = "0.4" + +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = false +incremental = false + +[features] +default = ["std"] +std = [] +nightly = ["compiler_builtins", "core", "alloc"] +compiler_builtins = [] +core = [] +alloc = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index a40c328..6661390 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,24 @@ ## Bellande Operating System Programming Language written in Rust - Can be used in any Operating System but Bellande Operating System works optimally when you use Bellronos Programming Language +## BELLRONOS Usage +## Website Crates +- https://crates.io/crates/bellronos + +### Installation +- `cargo add bellronos` + +``` +Name: bellronos +Version: 0.0.1 +Summary: Bellande Operating System Scripting Language +Home-page: github.com/Architecture-Mechanism/bellronos +Author: Ronaldson Bellande +Author-email: ronaldsonbellande@gmail.com +License: GNU General Public License v3.0 +``` ## License -bellronos BellandeOS Programming Language is distributed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), see [LICENSE](https://github.com/Architecture-Mechanism/bellronos/blob/main/LICENSE) and [NOTICE](https://github.com/Architecture-Mechanism/bellronos/blob/main/LICENSE) for more information. +Bellronos BellandeOS Programming Language is distributed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), see [LICENSE](https://github.com/Architecture-Mechanism/bellronos/blob/main/LICENSE) and [NOTICE](https://github.com/Architecture-Mechanism/bellronos/blob/main/LICENSE) for more information. diff --git a/bellronos_file/hello_world.bellronos b/bellronos_file/hello_world.bellronos new file mode 100644 index 0000000..6ebea4e --- /dev/null +++ b/bellronos_file/hello_world.bellronos @@ -0,0 +1,100 @@ +# example.bellronos + +import math +import io +import string + +define calculate_circle_area(radius: float) -> float: + return math.pi * radius * radius + +define greet(name: string) -> none: + io.print("Hello, " + string.to_upper(name) + "!") + +class Person: + define __init__(self, name: string, age: int) -> none: + self.name = name + self.age = age + + define introduce(self) -> none: + io.print("My name is " + self.name + " and I'm " + string.to_string(self.age) + " years old.") + +define main() -> none: + # Basic operations and function calls + set radius to 5.0 + set area to calculate_circle_area(radius) + io.print("The area of a circle with radius " + string.to_string(radius) + " is " + string.to_string(area)) + + greet("Bellronos") + + # Class usage + set person to Person("Alice", 30) + person.introduce() + + # Closures + set multiplier to closure (x: int) -> int: return x * 2 + io.print("Doubled 5: " + string.to_string(multiplier(5))) + + # Generators + define count_to(n: int) -> generator: + set i to 0 + while i < n: + yield i + set i to i + 1 + + for num in count_to(5): + io.print("Generated number: " + string.to_string(num)) + + # Async/await (simulated) + async define fetch_data() -> string: + # Simulating an asynchronous operation + io.print("Fetching data...") + return "Data fetched successfully" + + async define process_data() -> none: + set result to await fetch_data() + io.print("Processing result: " + result) + + process_data() + + # Language interoperability + set c_code to " + #include + int main() { + printf(\"Hello from C!\\n\"); + return 0; + } + " + io.print(execute_c(c_code)) + + set python_code to " +print('Hello from Python!') + " + io.print(execute_python(python_code)) + + set js_code to " +console.log('Hello from JavaScript!'); + " + io.print(execute_javascript(js_code)) + + set java_code to " +public class Temp { + public static void main(String[] args) { + System.out.println(\"Hello from Java!\"); + } +} + " + io.print(execute_java(java_code)) + + set rust_code to " +fn main() { + println!(\"Hello from Rust!\"); +} + " + io.print(execute_rust(rust_code)) + + set swift_code to " +print(\"Hello from Swift!\") + " + io.print(execute_swift(swift_code)) + +main() diff --git a/dependencies.txt b/dependencies.txt new file mode 100644 index 0000000..cd2a380 --- /dev/null +++ b/dependencies.txt @@ -0,0 +1,22 @@ +glob = "0.3.1" +tempfile = "3.3.0" +shellexpand = "3.1.0" +meval = "0.2.0" +reqwest = { version = "0.11", features = ["json", "blocking"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +anyhow = "1.0" +clap = { version = "4.0", features = ["derive"] } +regex = "1.7" +lazy_static = "1.4" +log = "0.4" +env_logger = "0.10" +tokio = { version = "1.0", features = ["full"] } +async-trait = "0.1" +futures = "0.3" +chrono = "0.4" +rand = "0.8" +assert_cmd = "2.0" +predicates = "3.0" +criterion = "0.4" diff --git a/git_scripts/.gitignore b/git_scripts/.gitignore new file mode 100644 index 0000000..e5a7a9c --- /dev/null +++ b/git_scripts/.gitignore @@ -0,0 +1,3 @@ +fix_errors.sh +push.sh +repository_recal.sh diff --git a/make_rust_executable.bellos b/make_rust_executable.bellos new file mode 100755 index 0000000..aac4b04 --- /dev/null +++ b/make_rust_executable.bellos @@ -0,0 +1 @@ +bellande_rust_executable -d dependencies.txt -s src -m bellronos.rs -o executable/bellronos diff --git a/make_rust_executable.sh b/make_rust_executable.sh new file mode 100755 index 0000000..aac4b04 --- /dev/null +++ b/make_rust_executable.sh @@ -0,0 +1 @@ +bellande_rust_executable -d dependencies.txt -s src -m bellronos.rs -o executable/bellronos diff --git a/src/ast/ast.rs b/src/ast/ast.rs new file mode 100644 index 0000000..45615f0 --- /dev/null +++ b/src/ast/ast.rs @@ -0,0 +1,111 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::type_system::type_system::Type; + +#[derive(Clone, PartialEq, Debug)] +pub enum ASTNode { + Module { + body: Vec, + }, + Import { + names: Vec, + }, + FunctionDef { + name: String, + args: Vec<(String, Type)>, + return_type: Type, + body: Vec, + }, + ClassDef { + name: String, + methods: Vec, + }, + Assign { + target: String, + value: Box, + }, + Expr { + value: Box, + }, + Call { + func: String, + args: Vec, + }, + Str { + value: String, + }, + Num { + value: f64, + }, + Bool { + value: bool, + }, + Name { + id: String, + }, + BinOp { + left: Box, + op: String, + right: Box, + }, + If { + condition: Box, + body: Vec, + orelse: Vec, + }, + While { + condition: Box, + body: Vec, + }, + For { + target: String, + iter: Box, + body: Vec, + }, + Return { + value: Option>, + }, + Closure { + params: Vec<(String, Type)>, + body: Box, + }, + Generator { + body: Vec, + }, + Yield { + value: Box, + }, + Async { + body: Vec, + }, + Await { + value: Box, + }, + List { + elements: Vec, + }, + Dict { + pairs: Vec<(ASTNode, ASTNode)>, + }, + Attribute { + value: Box, + attr: String, + }, + InteropCall { + language: String, + code: String, + }, +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs new file mode 100644 index 0000000..851c0bc --- /dev/null +++ b/src/ast/mod.rs @@ -0,0 +1 @@ +pub mod ast; diff --git a/src/bellronos.rs b/src/bellronos.rs new file mode 100644 index 0000000..5bafb0b --- /dev/null +++ b/src/bellronos.rs @@ -0,0 +1,53 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +mod ast; +mod error; +mod interop; +mod interpreter; +mod lexer; +mod package_manager; +mod parser; +mod standard_library; +mod type_system; + +use error::error::BellronosError; +use interpreter::interpreter::BellronosInterpreter; +use package_manager::package_manager::PackageManager; +use std::env; +use std::fs; + +fn main() -> Result<(), BellronosError> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + println!("Usage: bellronos [--install ]"); + return Ok(()); + } + + if args[1] == "--install" && args.len() == 3 { + let package_manager = PackageManager::new("packages".to_string()); + package_manager.install_package(&args[2])?; + println!("Package {} installed successfully", args[2]); + return Ok(()); + } + + let filename = &args[1]; + let contents = fs::read_to_string(filename)?; + + let mut interpreter = BellronosInterpreter::new(); + interpreter.run(&contents, filename)?; + + Ok(()) +} diff --git a/src/error/error.rs b/src/error/error.rs new file mode 100644 index 0000000..e9162cd --- /dev/null +++ b/src/error/error.rs @@ -0,0 +1,49 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::error::Error; +use std::fmt; +use std::io; + +#[derive(Debug)] +pub enum BellronosError { + IO(io::Error), + Parser(String), + Type(String), + Runtime(String), + Network(String), + Package(String), +} + +impl fmt::Display for BellronosError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BellronosError::IO(err) => write!(f, "IO error: {}", err), + BellronosError::Parser(msg) => write!(f, "Parser error: {}", msg), + BellronosError::Type(msg) => write!(f, "Type error: {}", msg), + BellronosError::Runtime(msg) => write!(f, "Runtime error: {}", msg), + BellronosError::Network(msg) => write!(f, "Network error: {}", msg), + BellronosError::Package(msg) => write!(f, "Package error: {}", msg), + } + } +} + +impl Error for BellronosError {} + +impl From for BellronosError { + fn from(err: io::Error) -> BellronosError { + BellronosError::IO(err) + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..a91e735 --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1 @@ +pub mod error; diff --git a/src/interop/interop.rs b/src/interop/interop.rs new file mode 100644 index 0000000..b94de84 --- /dev/null +++ b/src/interop/interop.rs @@ -0,0 +1,147 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::error::error::BellronosError; +use crate::interpreter::interpreter::Value; +use std::process::Command; + +pub struct LanguageInterop; + +impl LanguageInterop { + pub fn new() -> Self { + LanguageInterop + } + + pub fn execute(&self, language: &str, code: &str) -> Result { + match language { + "c" => self.execute_c(code), + "python" => self.execute_python(code), + "javascript" => self.execute_javascript(code), + "java" => self.execute_java(code), + "rust" => self.execute_rust(code), + "swift" => self.execute_swift(code), + _ => Err(BellronosError::Runtime(format!( + "Unsupported language: {}", + language + ))), + } + } + + fn execute_c(&self, code: &str) -> Result { + let temp_file = "temp.c"; + std::fs::write(temp_file, code)?; + + let output = Command::new("gcc") + .args(&[temp_file, "-o", "temp_c"]) + .output()?; + + if !output.status.success() { + return Err(BellronosError::Runtime(format!( + "C compilation error: {}", + String::from_utf8_lossy(&output.stderr) + ))); + } + + let output = Command::new("./temp_c").output()?; + std::fs::remove_file(temp_file)?; + std::fs::remove_file("temp_c")?; + + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } + + fn execute_python(&self, code: &str) -> Result { + let output = Command::new("python").arg("-c").arg(code).output()?; + + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } + + fn execute_javascript(&self, code: &str) -> Result { + let output = Command::new("node").arg("-e").arg(code).output()?; + + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } + + fn execute_java(&self, code: &str) -> Result { + let temp_file = "Temp.java"; + std::fs::write(temp_file, code)?; + + let output = Command::new("javac").arg(temp_file).output()?; + + if !output.status.success() { + return Err(BellronosError::Runtime(format!( + "Java compilation error: {}", + String::from_utf8_lossy(&output.stderr) + ))); + } + + let output = Command::new("java").arg("Temp").output()?; + + std::fs::remove_file(temp_file)?; + std::fs::remove_file("Temp.class")?; + + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } + + fn execute_rust(&self, code: &str) -> Result { + let temp_file = "temp.rs"; + std::fs::write(temp_file, code)?; + + let output = Command::new("rustc").arg(temp_file).output()?; + + if !output.status.success() { + return Err(BellronosError::Runtime(format!( + "Rust compilation error: {}", + String::from_utf8_lossy(&output.stderr) + ))); + } + + let output = Command::new("./temp").output()?; + + std::fs::remove_file(temp_file)?; + std::fs::remove_file("temp")?; + + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } + + fn execute_swift(&self, code: &str) -> Result { + let temp_file = "temp.swift"; + std::fs::write(temp_file, code)?; + + let output = Command::new("swift").arg(temp_file).output()?; + + std::fs::remove_file(temp_file)?; + + if output.status.success() { + Ok(Value::String( + String::from_utf8_lossy(&output.stdout).to_string(), + )) + } else { + Err(BellronosError::Runtime(format!( + "Swift execution error: {}", + String::from_utf8_lossy(&output.stderr) + ))) + } + } +} diff --git a/src/interop/mod.rs b/src/interop/mod.rs new file mode 100644 index 0000000..9d3c275 --- /dev/null +++ b/src/interop/mod.rs @@ -0,0 +1 @@ +pub mod interop; diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs new file mode 100644 index 0000000..0b036c2 --- /dev/null +++ b/src/interpreter/interpreter.rs @@ -0,0 +1,450 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::ast::ast::ASTNode; +use crate::error::error::BellronosError; +use crate::interop::interop::LanguageInterop; +use crate::lexer::lexer::Lexer; +use crate::package_manager::package_manager::PackageManager; +use crate::parser::parser::Parser; +use crate::standard_library::standard_library::StandardLibrary; +use crate::type_system::type_system::TypeChecker; +use std::cell::RefCell; +use std::collections::HashMap; +use std::f64; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +type Environment = Rc>>; + +#[derive(Clone)] +pub enum Value { + Int(i64), + Float(f64), + String(String), + Bool(bool), + List(Vec), + Dict(HashMap), + Function(Vec, Vec, Environment), + Class { + methods: HashMap, + }, + Instance { + class: String, + attributes: HashMap, + }, + Closure(Vec, Vec, Environment), + Generator(Vec, Environment, usize), + None, +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Value::Int(a), Value::Int(b)) => a == b, + (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON, + (Value::String(a), Value::String(b)) => a == b, + (Value::Bool(a), Value::Bool(b)) => a == b, + (Value::List(a), Value::List(b)) => a == b, + (Value::Dict(a), Value::Dict(b)) => a == b, + // For Function, Class, Instance, Closure, and Generator, compare memory addresses + (Value::Function(_, _, _), Value::Function(_, _, _)) => std::ptr::eq(self, other), + (Value::Class { .. }, Value::Class { .. }) => std::ptr::eq(self, other), + (Value::Instance { .. }, Value::Instance { .. }) => std::ptr::eq(self, other), + (Value::Closure(_, _, _), Value::Closure(_, _, _)) => std::ptr::eq(self, other), + (Value::Generator(_, _, _), Value::Generator(_, _, _)) => std::ptr::eq(self, other), + (Value::None, Value::None) => true, + _ => false, + } + } +} + +impl Eq for Value {} + +impl Hash for Value { + fn hash(&self, state: &mut H) { + match self { + Value::Int(i) => i.hash(state), + Value::Float(f) => f.to_bits().hash(state), + Value::String(s) => s.hash(state), + Value::Bool(b) => b.hash(state), + Value::List(l) => l.hash(state), + Value::Dict(d) => { + for (k, v) in d { + k.hash(state); + v.hash(state); + } + } + // For Function, Class, Instance, Closure, and Generator, hash memory addresses + Value::Function(_, _, _) + | Value::Class { .. } + | Value::Instance { .. } + | Value::Closure(_, _, _) + | Value::Generator(_, _, _) => (self as *const Value).hash(state), + Value::None => 0.hash(state), + } + } +} + +pub struct BellronosInterpreter { + global_env: Environment, + type_checker: TypeChecker, + stdlib: StandardLibrary, + package_manager: PackageManager, + language_interop: LanguageInterop, +} + +impl BellronosInterpreter { + pub fn new() -> Self { + let global_env = Rc::new(RefCell::new(HashMap::new())); + let type_checker = TypeChecker::new(); + let stdlib = StandardLibrary::new(); + let package_manager = PackageManager::new("packages".to_string()); + let language_interop = LanguageInterop::new(); + + BellronosInterpreter { + global_env, + type_checker, + stdlib, + package_manager, + language_interop, + } + } + + pub fn run(&mut self, code: &str, _filename: &str) -> Result<(), BellronosError> { + let mut lexer = Lexer::new(code); + let tokens = lexer.tokenize()?; + let mut parser = Parser::new(tokens); + let ast = parser.parse()?; + + self.interpret(&ast)?; + + Ok(()) + } + + fn interpret(&mut self, node: &ASTNode) -> Result { + match node { + ASTNode::Module { body } => { + let mut result = Value::None; + for stmt in body { + result = self.interpret(stmt)?; + } + Ok(result) + } + ASTNode::Import { names } => { + for name in names { + if let Some(module) = self.stdlib.get_module(name) { + self.global_env + .borrow_mut() + .insert(name.clone(), Value::Dict(module.clone())); + } else { + let package_code = self.package_manager.load_package(name)?; + self.run(&package_code, name)?; + } + } + Ok(Value::None) + } + ASTNode::FunctionDef { + name, + args, + return_type: _, + body, + } => { + let func = Value::Function( + args.iter().map(|(name, _)| name.clone()).collect(), + body.clone(), + Rc::clone(&self.global_env), + ); + self.global_env.borrow_mut().insert(name.clone(), func); + Ok(Value::None) + } + ASTNode::ClassDef { name, methods } => { + let mut class_methods = HashMap::new(); + for method in methods { + if let ASTNode::FunctionDef { + name: method_name, + args, + return_type: _, + body, + } = method + { + let method_func = Value::Function( + args.iter().map(|(name, _)| name.clone()).collect(), + body.clone(), + Rc::clone(&self.global_env), + ); + class_methods.insert(method_name.clone(), method_func); + } + } + let class = Value::Class { + methods: class_methods, + }; + self.global_env.borrow_mut().insert(name.clone(), class); + Ok(Value::None) + } + ASTNode::Assign { target, value } => { + let val = self.interpret(value)?; + self.global_env.borrow_mut().insert(target.clone(), val); + Ok(Value::None) + } + ASTNode::Expr { value } => self.interpret(value), + ASTNode::Call { func, args } => { + let f = self.global_env.borrow().get(func).cloned().ok_or_else(|| { + BellronosError::Runtime(format!("Undefined function: {}", func)) + })?; + match f { + Value::Function(params, body, env) => { + let mut local_env = env.borrow().clone(); + for (param, arg) in params.iter().zip(args) { + local_env.insert(param.clone(), self.interpret(arg)?); + } + let local_env = Rc::new(RefCell::new(local_env)); + let mut result = Value::None; + for stmt in &body { + result = self.interpret_with_env(stmt, &local_env)?; + } + Ok(result) + } + Value::Class { methods: _ } => { + let instance = Value::Instance { + class: func.clone(), + attributes: HashMap::new(), + }; + Ok(instance) + } + _ => Err(BellronosError::Runtime(format!("{} is not callable", func))), + } + } + ASTNode::Str { value } => Ok(Value::String(value.clone())), + ASTNode::Num { value } => Ok(Value::Float(*value)), + ASTNode::Bool { value } => Ok(Value::Bool(*value)), + ASTNode::Name { id } => self + .global_env + .borrow() + .get(id) + .cloned() + .ok_or_else(|| BellronosError::Runtime(format!("Undefined variable: {}", id))), + ASTNode::BinOp { left, op, right } => { + let left_val = self.interpret(left)?; + let right_val = self.interpret(right)?; + match (left_val, op.as_str(), right_val) { + (Value::Int(l), "+", Value::Int(r)) => Ok(Value::Int(l + r)), + (Value::Float(l), "+", Value::Float(r)) => Ok(Value::Float(l + r)), + (Value::String(l), "+", Value::String(r)) => Ok(Value::String(l + &r)), + (Value::Int(l), "-", Value::Int(r)) => Ok(Value::Int(l - r)), + (Value::Float(l), "-", Value::Float(r)) => Ok(Value::Float(l - r)), + (Value::Int(l), "*", Value::Int(r)) => Ok(Value::Int(l * r)), + (Value::Float(l), "*", Value::Float(r)) => Ok(Value::Float(l * r)), + (Value::Int(l), "/", Value::Int(r)) => Ok(Value::Float(l as f64 / r as f64)), + (Value::Float(l), "/", Value::Float(r)) => Ok(Value::Float(l / r)), + (Value::Int(l), ">", Value::Int(r)) => Ok(Value::Bool(l > r)), + (Value::Float(l), ">", Value::Float(r)) => Ok(Value::Bool(l > r)), + (Value::Int(l), "<", Value::Int(r)) => Ok(Value::Bool(l < r)), + (Value::Float(l), "<", Value::Float(r)) => Ok(Value::Bool(l < r)), + (Value::Int(l), ">=", Value::Int(r)) => Ok(Value::Bool(l >= r)), + (Value::Float(l), ">=", Value::Float(r)) => Ok(Value::Bool(l >= r)), + (Value::Int(l), "<=", Value::Int(r)) => Ok(Value::Bool(l <= r)), + (Value::Float(l), "<=", Value::Float(r)) => Ok(Value::Bool(l <= r)), + (Value::Int(l), "==", Value::Int(r)) => Ok(Value::Bool(l == r)), + (Value::Float(l), "==", Value::Float(r)) => Ok(Value::Bool(l == r)), + (Value::String(l), "==", Value::String(r)) => Ok(Value::Bool(l == r)), + (Value::Bool(l), "==", Value::Bool(r)) => Ok(Value::Bool(l == r)), + _ => Err(BellronosError::Runtime(format!( + "Unsupported operation: {:?} {} {:?}", + left, op, right + ))), + } + } + + ASTNode::If { + condition, + body, + orelse, + } => { + let cond_value = self.interpret(condition)?; + if let Value::Bool(true) = cond_value { + for stmt in body { + self.interpret(stmt)?; + } + } else { + for stmt in orelse { + self.interpret(stmt)?; + } + } + Ok(Value::None) + } + ASTNode::While { condition, body } => { + loop { + let cond_value = self.interpret(condition)?; + if let Value::Bool(true) = cond_value { + for stmt in body { + self.interpret(stmt)?; + } + } else { + break; + } + } + Ok(Value::None) + } + ASTNode::For { target, iter, body } => { + let iter_value = self.interpret(iter)?; + if let Value::List(items) = iter_value { + for item in items { + self.global_env.borrow_mut().insert(target.clone(), item); + for stmt in body { + self.interpret(stmt)?; + } + } + } else { + return Err(BellronosError::Runtime( + "For loop iterable must be a list".to_string(), + )); + } + Ok(Value::None) + } + ASTNode::Return { value } => { + if let Some(v) = value { + self.interpret(v) + } else { + Ok(Value::None) + } + } + ASTNode::Closure { params, body } => Ok(Value::Closure( + params.iter().map(|(name, _)| name.clone()).collect(), + vec![*body.clone()], + Rc::clone(&self.global_env), + )), + ASTNode::Generator { body } => Ok(Value::Generator( + body.clone(), + Rc::clone(&self.global_env), + 0, + )), + ASTNode::Yield { value: _ } => Err(BellronosError::Runtime( + "Yield outside of generator".to_string(), + )), + ASTNode::Async { body } => { + let mut result = Value::None; + for stmt in body { + result = self.interpret(stmt)?; + } + Ok(result) + } + ASTNode::Await { value } => self.interpret(value), + ASTNode::List { elements } => { + let mut list = Vec::new(); + for elem in elements { + list.push(self.interpret(elem)?); + } + Ok(Value::List(list)) + } + ASTNode::Dict { pairs } => { + let mut dict = HashMap::new(); + for (key, value) in pairs { + if let Value::String(k) = self.interpret(key)? { + let v = self.interpret(value)?; + dict.insert(k, v); + } else { + return Err(BellronosError::Runtime( + "Dictionary keys must be strings".to_string(), + )); + } + } + Ok(Value::Dict(dict)) + } + ASTNode::Attribute { value, attr } => { + let obj = self.interpret(value)?; + match obj { + Value::Instance { class, attributes } => { + if let Some(attr_value) = attributes.get(attr) { + Ok(attr_value.clone()) + } else if let Some(Value::Class { methods }) = + self.global_env.borrow().get(&class) + { + if let Some(method) = methods.get(attr) { + Ok(method.clone()) + } else { + Err(BellronosError::Runtime(format!( + "Attribute '{}' not found on instance of class '{}'", + attr, class + ))) + } + } else { + Err(BellronosError::Runtime(format!( + "Class '{}' not found", + class + ))) + } + } + _ => Err(BellronosError::Runtime(format!( + "Cannot access attribute '{}' on non-instance type", + attr + ))), + } + } + ASTNode::InteropCall { language, code } => { + self.language_interop.execute(language, code) + } + } + } + + fn interpret_with_env( + &mut self, + node: &ASTNode, + env: &Environment, + ) -> Result { + let old_env = self.global_env.clone(); + *self.global_env.borrow_mut() = env.borrow().clone(); + let result = self.interpret(node); + *self.global_env.borrow_mut() = old_env.borrow().clone(); + result + } +} + +impl std::fmt::Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Int(i) => write!(f, "{}", i), + Value::Float(fl) => write!(f, "{}", fl), + Value::String(s) => write!(f, "\"{}\"", s), + Value::Bool(b) => write!(f, "{}", b), + Value::List(l) => { + write!(f, "[")?; + for (i, item) in l.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{:?}", item)?; + } + write!(f, "]") + } + Value::Dict(d) => { + write!(f, "{{")?; + for (i, (k, v)) in d.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "\"{}\": {:?}", k, v)?; + } + write!(f, "}}") + } + Value::Function(_, _, _) => write!(f, ""), + Value::Class { .. } => write!(f, ""), + Value::Instance { class, .. } => write!(f, "", class), + Value::Closure(_, _, _) => write!(f, ""), + Value::Generator(_, _, _) => write!(f, ""), + Value::None => write!(f, "None"), + } + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 0000000..144a532 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1 @@ +pub mod interpreter; diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs new file mode 100644 index 0000000..47eee99 --- /dev/null +++ b/src/lexer/lexer.rs @@ -0,0 +1,308 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::error::error::BellronosError; + +#[derive(Debug, Clone, PartialEq)] +pub enum Token { + Import, + Define, + Class, + Set, + To, + If, + Else, + While, + For, + In, + Return, + Async, + Await, + Yield, + Closure, + True, + False, + Identifier(String), + String(String), + Number(f64), + LeftParen, + RightParen, + LeftBrace, + RightBrace, + LeftBracket, + RightBracket, + Colon, + Comma, + Dot, + Plus, + Minus, + Multiply, + Divide, + Equals, + NotEquals, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual, + Arrow, + Newline, + EOF, +} + +pub struct Lexer { + input: Vec, + position: usize, + line: usize, + column: usize, +} + +impl Lexer { + pub fn new(input: &str) -> Self { + Lexer { + input: input.chars().collect(), + position: 0, + line: 1, + column: 1, + } + } + + pub fn tokenize(&mut self) -> Result, BellronosError> { + let mut tokens = Vec::new(); + while let Some(token) = self.next_token()? { + tokens.push(token); + } + tokens.push(Token::EOF); + Ok(tokens) + } + + fn next_token(&mut self) -> Result, BellronosError> { + self.skip_whitespace(); + + if self.position >= self.input.len() { + return Ok(None); + } + + let token = match self.current_char() { + '(' => { + self.advance(); + Ok(Some(Token::LeftParen)) + } + ')' => { + self.advance(); + Ok(Some(Token::RightParen)) + } + '{' => { + self.advance(); + Ok(Some(Token::LeftBrace)) + } + '}' => { + self.advance(); + Ok(Some(Token::RightBrace)) + } + '[' => { + self.advance(); + Ok(Some(Token::LeftBracket)) + } + ']' => { + self.advance(); + Ok(Some(Token::RightBracket)) + } + ':' => { + self.advance(); + Ok(Some(Token::Colon)) + } + ',' => { + self.advance(); + Ok(Some(Token::Comma)) + } + '.' => { + self.advance(); + Ok(Some(Token::Dot)) + } + '+' => { + self.advance(); + Ok(Some(Token::Plus)) + } + '-' => { + self.advance(); + if self.current_char() == '>' { + self.advance(); + Ok(Some(Token::Arrow)) + } else { + Ok(Some(Token::Minus)) + } + } + '*' => { + self.advance(); + Ok(Some(Token::Multiply)) + } + '/' => { + self.advance(); + Ok(Some(Token::Divide)) + } + '=' => { + self.advance(); + if self.current_char() == '=' { + self.advance(); + Ok(Some(Token::Equals)) + } else { + Ok(Some(Token::Set)) + } + } + '!' => { + self.advance(); + if self.current_char() == '=' { + self.advance(); + Ok(Some(Token::NotEquals)) + } else { + Err(BellronosError::Parser(format!( + "Unexpected character: {}", + self.current_char() + ))) + } + } + '<' => { + self.advance(); + if self.current_char() == '=' { + self.advance(); + Ok(Some(Token::LessThanOrEqual)) + } else { + Ok(Some(Token::LessThan)) + } + } + '>' => { + self.advance(); + if self.current_char() == '=' { + self.advance(); + Ok(Some(Token::GreaterThanOrEqual)) + } else { + Ok(Some(Token::GreaterThan)) + } + } + '\n' => { + self.advance_line(); + Ok(Some(Token::Newline)) + } + '"' => self.tokenize_string(), + '0'..='9' => self.tokenize_number(), + 'a'..='z' | 'A'..='Z' | '_' => self.tokenize_identifier(), + '#' => { + while self.current_char() != '\n' && self.position < self.input.len() { + self.advance(); + } + self.next_token() + } + _ => Err(BellronosError::Parser(format!( + "Unexpected character: {}", + self.current_char() + ))), + }?; + + Ok(token) + } + + fn tokenize_string(&mut self) -> Result, BellronosError> { + self.advance(); // Skip opening quote + let start = self.position; + while self.current_char() != '"' && self.position < self.input.len() { + if self.current_char() == '\n' { + return Err(BellronosError::Parser(format!( + "Unexpected character: {}", + self.current_char() + ))); + } + self.advance(); + } + if self.position >= self.input.len() { + return Err(BellronosError::Parser(format!( + "Unexpected character: {}", + self.current_char() + ))); + } + let value: String = self.input[start..self.position].iter().collect(); + self.advance(); // Skip closing quote + Ok(Some(Token::String(value))) + } + + fn tokenize_number(&mut self) -> Result, BellronosError> { + let start = self.position; + while self.position < self.input.len() + && (self.current_char().is_digit(10) || self.current_char() == '.') + { + self.advance(); + } + let value: String = self.input[start..self.position].iter().collect(); + value + .parse::() + .map(|n| Some(Token::Number(n))) + .map_err(|_| { + BellronosError::Parser(format!("Unexpected character: {}", self.current_char())) + }) + } + + fn tokenize_identifier(&mut self) -> Result, BellronosError> { + let start = self.position; + while self.position < self.input.len() + && (self.current_char().is_alphanumeric() || self.current_char() == '_') + { + self.advance(); + } + let value: String = self.input[start..self.position].iter().collect(); + Ok(Some(match value.as_str() { + "import" => Token::Import, + "define" => Token::Define, + "class" => Token::Class, + "set" => Token::Set, + "to" => Token::To, + "if" => Token::If, + "else" => Token::Else, + "while" => Token::While, + "for" => Token::For, + "in" => Token::In, + "return" => Token::Return, + "async" => Token::Async, + "await" => Token::Await, + "yield" => Token::Yield, + "closure" => Token::Closure, + "true" => Token::True, + "false" => Token::False, + _ => Token::Identifier(value), + })) + } + + fn current_char(&self) -> char { + self.input[self.position] + } + + fn advance(&mut self) { + self.position += 1; + self.column += 1; + } + + fn advance_line(&mut self) { + self.position += 1; + self.line += 1; + self.column = 1; + } + + fn skip_whitespace(&mut self) { + while self.position < self.input.len() + && self.current_char().is_whitespace() + && self.current_char() != '\n' + { + self.advance(); + } + } +} diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs new file mode 100644 index 0000000..fc84151 --- /dev/null +++ b/src/lexer/mod.rs @@ -0,0 +1 @@ +pub mod lexer; diff --git a/src/package_manager/mod.rs b/src/package_manager/mod.rs new file mode 100644 index 0000000..d3106a3 --- /dev/null +++ b/src/package_manager/mod.rs @@ -0,0 +1 @@ +pub mod package_manager; diff --git a/src/package_manager/package_manager.rs b/src/package_manager/package_manager.rs new file mode 100644 index 0000000..d230637 --- /dev/null +++ b/src/package_manager/package_manager.rs @@ -0,0 +1,250 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::error::error::BellronosError; +use reqwest; +use serde::{Deserialize, Serialize}; +use std::fs::{self, File}; +use std::io::{Error as IoError, Write}; +use std::path::PathBuf; + +const PACKAGE_REGISTRY_URL: &str = + "https://bellande-architecture-mechanism-research-innovation-center.org/bellronos/packages"; +const GITHUB_REPO_URL: &str = "https://github.com/Architecture-Mechanism/bellronos_package_manager"; + +#[derive(Serialize, Deserialize)] +struct PackageMetadata { + name: String, + version: String, + dependencies: Vec, +} + +pub struct PackageManager { + package_dir: PathBuf, +} + +impl PackageManager { + pub fn new(package_dir: String) -> Self { + PackageManager { + package_dir: PathBuf::from(package_dir), + } + } + + pub fn install_package(&self, package_name: &str) -> Result<(), BellronosError> { + println!("Installing package: {}", package_name); + + // Fetch package metadata + let metadata = self.fetch_package_metadata(package_name)?; + + // Download package + let package_content = self.download_package(&metadata)?; + + // Install dependencies + for dependency in &metadata.dependencies { + self.install_package(dependency)?; + } + + // Write package content + let package_path = self + .package_dir + .join(&metadata.name) + .with_extension("bellronos"); + let mut file = File::create(&package_path).map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!("Failed to create package file: {}", e), + )) + })?; + file.write_all(package_content.as_bytes()).map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!("Failed to write package content: {}", e), + )) + })?; + + println!("Successfully installed package: {}", package_name); + Ok(()) + } + + pub fn load_package(&self, package_name: &str) -> Result { + let package_path = self + .package_dir + .join(package_name) + .with_extension("bellronos"); + fs::read_to_string(&package_path).map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!( + "Failed to read package file {}: {}", + package_path.display(), + e + ), + )) + }) + } + + pub fn list_installed_packages(&self) -> Result, BellronosError> { + let entries = fs::read_dir(&self.package_dir).map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!( + "Failed to read package directory {}: {}", + self.package_dir.display(), + e + ), + )) + })?; + + let mut packages = Vec::new(); + for entry in entries { + let entry = entry.map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!("Failed to read directory entry: {}", e), + )) + })?; + if let Some(file_name) = entry.path().file_stem() { + if let Some(name) = file_name.to_str() { + packages.push(name.to_string()); + } + } + } + + Ok(packages) + } + + fn fetch_package_metadata( + &self, + package_name: &str, + ) -> Result { + let url = format!("{}/{}/metadata.json", PACKAGE_REGISTRY_URL, package_name); + let response = reqwest::blocking::get(&url).map_err(|e| { + BellronosError::Network(format!("Failed to fetch package metadata: {}", e)) + })?; + + if !response.status().is_success() { + return Err(BellronosError::Network(format!( + "Failed to fetch package metadata. Status: {}", + response.status() + ))); + } + + response + .json::() + .map_err(|e| BellronosError::Parser(format!("Failed to parse package metadata: {}", e))) + } + + fn download_package(&self, metadata: &PackageMetadata) -> Result { + let url = format!( + "{}/{}/{}.bellronos", + PACKAGE_REGISTRY_URL, metadata.name, metadata.version + ); + let response = reqwest::blocking::get(&url) + .map_err(|e| BellronosError::Network(format!("Failed to download package: {}", e)))?; + + if !response.status().is_success() { + return Err(BellronosError::Network(format!( + "Failed to download package. Status: {}", + response.status() + ))); + } + + response + .text() + .map_err(|e| BellronosError::Network(format!("Failed to read package content: {}", e))) + } + + pub fn update_package(&self, package_name: &str) -> Result<(), BellronosError> { + let installed_packages = self.list_installed_packages()?; + if !installed_packages.contains(&package_name.to_string()) { + return Err(BellronosError::Package(format!( + "Package {} is not installed", + package_name + ))); + } + + // Fetch latest metadata + let metadata = self.fetch_package_metadata(package_name)?; + + // Check if update is needed + let current_version = self.get_installed_package_version(package_name)?; + if current_version == metadata.version { + println!("Package {} is already up to date", package_name); + return Ok(()); + } + + // Perform update + self.install_package(package_name) + } + + fn get_installed_package_version(&self, package_name: &str) -> Result { + let package_path = self + .package_dir + .join(package_name) + .with_extension("bellronos"); + let content = fs::read_to_string(&package_path).map_err(|e| { + BellronosError::IO(IoError::new( + e.kind(), + format!( + "Failed to read package file {}: {}", + package_path.display(), + e + ), + )) + })?; + + // Extract version from package content + content + .lines() + .find(|line| line.starts_with("# Version:")) + .and_then(|line| line.split(':').nth(1)) + .map(|version| version.trim().to_string()) + .ok_or_else(|| BellronosError::Parser("Failed to extract package version".to_string())) + } + + pub fn search_packages(&self, query: &str) -> Result, BellronosError> { + let url = format!("{}/search?q={}", PACKAGE_REGISTRY_URL, query); + let response = reqwest::blocking::get(&url) + .map_err(|e| BellronosError::Network(format!("Failed to search packages: {}", e)))?; + + if !response.status().is_success() { + return Err(BellronosError::Network(format!( + "Failed to search packages. Status: {}", + response.status() + ))); + } + + response + .json::>() + .map_err(|e| BellronosError::Parser(format!("Failed to parse search results: {}", e))) + } + + pub fn get_package_info(&self, package_name: &str) -> Result { + let url = format!("{}/{}/README.md", GITHUB_REPO_URL, package_name); + let response = reqwest::blocking::get(&url) + .map_err(|e| BellronosError::Network(format!("Failed to fetch package info: {}", e)))?; + + if !response.status().is_success() { + return Err(BellronosError::Network(format!( + "Failed to fetch package info. Status: {}", + response.status() + ))); + } + + response + .text() + .map_err(|e| BellronosError::Network(format!("Failed to read package info: {}", e))) + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..67c567f --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1 @@ +pub mod parser; diff --git a/src/parser/parser.rs b/src/parser/parser.rs new file mode 100644 index 0000000..df337a7 --- /dev/null +++ b/src/parser/parser.rs @@ -0,0 +1,455 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::ast::ast::ASTNode; +use crate::error::error::BellronosError; +use crate::lexer::lexer::Token; +use crate::type_system::type_system::Type; + +pub struct Parser { + tokens: Vec, + position: usize, +} + +impl Parser { + pub fn new(tokens: Vec) -> Self { + Parser { + tokens, + position: 0, + } + } + + pub fn parse(&mut self) -> Result { + let mut body = Vec::new(); + while self.position < self.tokens.len() { + body.push(self.parse_statement()?); + } + Ok(ASTNode::Module { body }) + } + + fn parse_statement(&mut self) -> Result { + match self.current_token() { + Token::Import => self.parse_import(), + Token::Define => self.parse_function_def(), + Token::Class => self.parse_class_def(), + Token::Set => self.parse_assignment(), + Token::If => self.parse_if(), + Token::While => self.parse_while(), + Token::For => self.parse_for(), + Token::Return => self.parse_return(), + Token::Async => self.parse_async(), + Token::Yield => self.parse_yield(), + Token::Closure => self.parse_closure(), + _ => self.parse_expression(), + } + } + + fn parse_import(&mut self) -> Result { + self.advance(); // Consume 'import' + let mut names = Vec::new(); + while let Token::Identifier(name) = self.current_token() { + names.push(name.clone()); + self.advance(); + if self.current_token() == Token::Comma { + self.advance(); + } else { + break; + } + } + self.expect_token(Token::Newline)?; + Ok(ASTNode::Import { names }) + } + + fn parse_function_def(&mut self) -> Result { + self.advance(); // Consume 'define' + let name = self.expect_identifier()?; + let args = self.parse_function_args()?; + let return_type = self.parse_return_type()?; + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + let body = self.parse_block()?; + Ok(ASTNode::FunctionDef { + name, + args, + return_type, + body, + }) + } + + fn parse_class_def(&mut self) -> Result { + self.advance(); // Consume 'class' + let name = self.expect_identifier()?; + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + let methods = self.parse_block()?; + Ok(ASTNode::ClassDef { name, methods }) + } + + fn parse_assignment(&mut self) -> Result { + self.advance(); // Consume 'set' + let target = self.expect_identifier()?; + self.expect_token(Token::To)?; + let value = Box::new(self.parse_expression()?); + self.expect_token(Token::Newline)?; + Ok(ASTNode::Assign { target, value }) + } + + fn parse_if(&mut self) -> Result { + self.advance(); // Consume 'if' + let condition = Box::new(self.parse_expression()?); + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + let body = self.parse_block()?; + let mut orelse = Vec::new(); + if self.current_token() == Token::Else { + self.advance(); + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + orelse = self.parse_block()?; + } + Ok(ASTNode::If { + condition, + body, + orelse, + }) + } + + fn parse_while(&mut self) -> Result { + self.advance(); // Consume 'while' + let condition = Box::new(self.parse_expression()?); + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + let body = self.parse_block()?; + Ok(ASTNode::While { condition, body }) + } + + fn parse_for(&mut self) -> Result { + self.advance(); // Consume 'for' + let target = self.expect_identifier()?; + self.expect_token(Token::In)?; + let iter = Box::new(self.parse_expression()?); + self.expect_token(Token::Colon)?; + self.expect_token(Token::Newline)?; + let body = self.parse_block()?; + Ok(ASTNode::For { target, iter, body }) + } + + fn parse_return(&mut self) -> Result { + self.advance(); // Consume 'return' + let value = if self.current_token() == Token::Newline { + None + } else { + Some(Box::new(self.parse_expression()?)) + }; + self.expect_token(Token::Newline)?; + Ok(ASTNode::Return { value }) + } + + fn parse_async(&mut self) -> Result { + self.advance(); // Consume 'async' + self.expect_token(Token::Define)?; + let function = self.parse_function_def()?; + if let ASTNode::FunctionDef { + name, + args, + return_type, + body, + } = function + { + Ok(ASTNode::Async { + body: vec![ASTNode::FunctionDef { + name, + args, + return_type, + body, + }], + }) + } else { + Err(BellronosError::Parser( + "Expected function definition after 'async'".to_string(), + )) + } + } + + fn parse_yield(&mut self) -> Result { + self.advance(); // Consume 'yield' + let value = Box::new(self.parse_expression()?); + self.expect_token(Token::Newline)?; + Ok(ASTNode::Yield { value }) + } + + fn parse_closure(&mut self) -> Result { + self.advance(); // Consume 'closure' + let params = self.parse_function_args()?; + self.expect_token(Token::Colon)?; + let body = Box::new(self.parse_expression()?); + Ok(ASTNode::Closure { params, body }) + } + + fn parse_expression(&mut self) -> Result { + self.parse_binary_operation() + } + + fn parse_binary_operation(&mut self) -> Result { + let mut left = self.parse_unary()?; + + while matches!( + self.current_token(), + Token::Plus + | Token::Minus + | Token::Multiply + | Token::Divide + | Token::Equals + | Token::NotEquals + | Token::LessThan + | Token::GreaterThan + | Token::LessThanOrEqual + | Token::GreaterThanOrEqual + ) { + let op = format!("{:?}", self.current_token()); + self.advance(); + let right = self.parse_unary()?; + left = ASTNode::BinOp { + left: Box::new(left), + op, + right: Box::new(right), + }; + } + + Ok(left) + } + + fn parse_unary(&mut self) -> Result { + match self.current_token() { + Token::Minus => { + self.advance(); + let expr = self.parse_unary()?; + Ok(ASTNode::BinOp { + left: Box::new(ASTNode::Num { value: 0.0 }), + op: "-".to_string(), + right: Box::new(expr), + }) + } + _ => self.parse_primary(), + } + } + + fn parse_primary(&mut self) -> Result { + match self.current_token() { + Token::Identifier(name) => { + self.advance(); + if self.current_token() == Token::LeftParen { + self.parse_function_call(&name) + } else { + Ok(ASTNode::Name { id: name }) + } + } + Token::String(value) => { + self.advance(); + Ok(ASTNode::Str { value }) + } + Token::Number(value) => { + self.advance(); + Ok(ASTNode::Num { value }) + } + Token::True => { + self.advance(); + Ok(ASTNode::Bool { value: true }) + } + Token::False => { + self.advance(); + Ok(ASTNode::Bool { value: false }) + } + Token::LeftParen => { + self.advance(); + let expr = self.parse_expression()?; + self.expect_token(Token::RightParen)?; + Ok(expr) + } + Token::LeftBracket => self.parse_list(), + Token::LeftBrace => self.parse_dict(), + _ => Err(BellronosError::Parser(format!( + "Unexpected token: {:?}", + self.current_token() + ))), + } + } + + fn parse_function_call(&mut self, name: &str) -> Result { + self.advance(); // Consume '(' + let mut args = Vec::new(); + if self.current_token() != Token::RightParen { + loop { + args.push(self.parse_expression()?); + if self.current_token() == Token::Comma { + self.advance(); + } else { + break; + } + } + } + self.expect_token(Token::RightParen)?; + Ok(ASTNode::Call { + func: name.to_string(), + args, + }) + } + + fn parse_list(&mut self) -> Result { + self.advance(); // Consume '[' + let mut elements = Vec::new(); + if self.current_token() != Token::RightBracket { + loop { + elements.push(self.parse_expression()?); + if self.current_token() == Token::Comma { + self.advance(); + } else { + break; + } + } + } + self.expect_token(Token::RightBracket)?; + Ok(ASTNode::List { elements }) + } + + fn parse_dict(&mut self) -> Result { + self.advance(); // Consume '{' + let mut pairs = Vec::new(); + if self.current_token() != Token::RightBrace { + loop { + let key = self.parse_expression()?; + self.expect_token(Token::Colon)?; + let value = self.parse_expression()?; + pairs.push((key, value)); + if self.current_token() == Token::Comma { + self.advance(); + } else { + break; + } + } + } + self.expect_token(Token::RightBrace)?; + Ok(ASTNode::Dict { pairs }) + } + + fn parse_function_args(&mut self) -> Result, BellronosError> { + let mut args = Vec::new(); + self.expect_token(Token::LeftParen)?; + if self.current_token() != Token::RightParen { + loop { + let name = self.expect_identifier()?; + self.expect_token(Token::Colon)?; + let type_ = self.parse_type()?; + args.push((name, type_)); + if self.current_token() == Token::Comma { + self.advance(); + } else { + break; + } + } + } + self.expect_token(Token::RightParen)?; + Ok(args) + } + + fn parse_return_type(&mut self) -> Result { + if self.current_token() == Token::Arrow { + self.advance(); + self.parse_type() + } else { + Ok(Type::None) + } + } + + fn parse_type(&mut self) -> Result { + match self.current_token() { + Token::Identifier(name) => { + self.advance(); + match name.as_str() { + "int" => Ok(Type::Int), + "float" => Ok(Type::Float), + "string" => Ok(Type::String), + "bool" => Ok(Type::Bool), + "list" => { + self.expect_token(Token::LeftBracket)?; + let inner_type = self.parse_type()?; + self.expect_token(Token::RightBracket)?; + Ok(Type::List(Box::new(inner_type))) + } + "dict" => { + self.expect_token(Token::LeftBrace)?; + let key_type = self.parse_type()?; + self.expect_token(Token::Colon)?; + let value_type = self.parse_type()?; + self.expect_token(Token::RightBrace)?; + Ok(Type::Dict(Box::new(key_type), Box::new(value_type))) + } + _ => Ok(Type::Custom(name)), + } + } + _ => Err(BellronosError::Parser("Expected type".to_string())), + } + } + + fn parse_block(&mut self) -> Result, BellronosError> { + let mut body = Vec::new(); + while self.current_token() != Token::EOF && !matches!(self.current_token(), Token::Else) { + body.push(self.parse_statement()?); + } + Ok(body) + } + + fn expect_identifier(&mut self) -> Result { + if let Token::Identifier(name) = self.current_token() { + self.advance(); + Ok(name) + } else { + Err(BellronosError::Parser(format!( + "Expected identifier, found {:?}", + self.current_token() + ))) + } + } + + fn expect_token(&mut self, expected: Token) -> Result<(), BellronosError> { + if self.current_token() == expected { + self.advance(); + Ok(()) + } else { + Err(BellronosError::Parser(format!( + "Expected {:?}, found {:?}", + expected, + self.current_token() + ))) + } + } + + fn current_token(&self) -> Token { + self.tokens + .get(self.position) + .cloned() + .unwrap_or(Token::EOF) + } + + fn advance(&mut self) { + self.position += 1; + } +} + +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/src/standard_library/mod.rs b/src/standard_library/mod.rs new file mode 100644 index 0000000..a812f0d --- /dev/null +++ b/src/standard_library/mod.rs @@ -0,0 +1 @@ +pub mod standard_library; diff --git a/src/standard_library/standard_library.rs b/src/standard_library/standard_library.rs new file mode 100644 index 0000000..e4e49cf --- /dev/null +++ b/src/standard_library/standard_library.rs @@ -0,0 +1,102 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::interpreter::interpreter::Value; +use std::collections::HashMap; + +pub struct StandardLibrary { + modules: HashMap>, +} + +impl StandardLibrary { + pub fn new() -> Self { + let mut stdlib = StandardLibrary { + modules: HashMap::new(), + }; + stdlib.init_math(); + stdlib.init_io(); + stdlib.init_string(); + stdlib + } + + fn init_math(&mut self) { + let mut math = HashMap::new(); + math.insert("pi".to_string(), Value::Float(std::f64::consts::PI)); + math.insert("e".to_string(), Value::Float(std::f64::consts::E)); + math.insert( + "sqrt".to_string(), + Value::Function( + vec!["x".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + self.modules.insert("math".to_string(), math); + } + + fn init_io(&mut self) { + let mut io = HashMap::new(); + io.insert( + "print".to_string(), + Value::Function( + vec!["args".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + io.insert( + "input".to_string(), + Value::Function( + vec!["prompt".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + self.modules.insert("io".to_string(), io); + } + + fn init_string(&mut self) { + let mut string = HashMap::new(); + string.insert( + "length".to_string(), + Value::Function( + vec!["s".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + string.insert( + "to_upper".to_string(), + Value::Function( + vec!["s".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + string.insert( + "to_lower".to_string(), + Value::Function( + vec!["s".to_string()], + vec![], + std::rc::Rc::new(std::cell::RefCell::new(HashMap::new())), + ), + ); + self.modules.insert("string".to_string(), string); + } + + pub fn get_module(&self, name: &str) -> Option<&HashMap> { + self.modules.get(name) + } +} diff --git a/src/type_system/mod.rs b/src/type_system/mod.rs new file mode 100644 index 0000000..a5097af --- /dev/null +++ b/src/type_system/mod.rs @@ -0,0 +1 @@ +pub mod type_system; diff --git a/src/type_system/type_system.rs b/src/type_system/type_system.rs new file mode 100644 index 0000000..0c21eee --- /dev/null +++ b/src/type_system/type_system.rs @@ -0,0 +1,507 @@ +// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::ast::ast::ASTNode; +use crate::error::error::BellronosError; +use std::collections::HashMap; +use std::str::FromStr; + +#[derive(Debug, Clone, PartialEq)] +pub enum InteropType { + CInt, + CFloat, + CString, + PyInt, + PyFloat, + PyString, + PyList, + PyDict, + JsNumber, + JsString, + JsBoolean, + JsArray, + JsObject, + Unknown, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum InteropLanguage { + C, + Python, + JavaScript, +} + +impl FromStr for InteropLanguage { + type Err = BellronosError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "c" => Ok(InteropLanguage::C), + "python" => Ok(InteropLanguage::Python), + "javascript" | "js" => Ok(InteropLanguage::JavaScript), + _ => Err(BellronosError::Type(format!( + "Unknown interop language: {}", + s + ))), + } + } +} + +impl InteropLanguage { + pub fn infer_type(&self, code: &str) -> Result { + match self { + InteropLanguage::C => { + if code.contains("int") { + Ok(InteropType::CInt) + } else if code.contains("float") { + Ok(InteropType::CFloat) + } else if code.contains("char*") { + Ok(InteropType::CString) + } else { + Ok(InteropType::Unknown) + } + } + InteropLanguage::Python => { + if code.contains("int(") { + Ok(InteropType::PyInt) + } else if code.contains("float(") { + Ok(InteropType::PyFloat) + } else if code.contains("str(") { + Ok(InteropType::PyString) + } else if code.contains("list(") { + Ok(InteropType::PyList) + } else if code.contains("dict(") { + Ok(InteropType::PyDict) + } else { + Ok(InteropType::Unknown) + } + } + InteropLanguage::JavaScript => { + if code.contains("Number(") { + Ok(InteropType::JsNumber) + } else if code.contains("String(") { + Ok(InteropType::JsString) + } else if code.contains("Boolean(") { + Ok(InteropType::JsBoolean) + } else if code.contains("Array(") { + Ok(InteropType::JsArray) + } else if code.contains("Object(") { + Ok(InteropType::JsObject) + } else { + Ok(InteropType::Unknown) + } + } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Int, + Float, + String, + Bool, + List(Box), + Dict(Box, Box), + Function(Vec, Box), + Class(String), + Instance(String), + None, + Any, + Custom(String), + Interop(InteropType), +} + +#[derive(Clone)] +pub struct TypeChecker { + type_env: HashMap, + class_env: HashMap>, +} + +impl TypeChecker { + pub fn new() -> Self { + TypeChecker { + type_env: HashMap::new(), + class_env: HashMap::new(), + } + } + + pub fn check(&mut self, node: &ASTNode) -> Result { + match node { + ASTNode::Module { body } => { + for stmt in body { + self.check(stmt)?; + } + Ok(Type::None) + } + ASTNode::Import { names } => { + for name in names { + self.type_env.insert(name.clone(), Type::Any); + } + Ok(Type::None) + } + ASTNode::FunctionDef { + name, + args, + return_type, + body, + } => { + let arg_types: Vec = args.iter().map(|(_, t)| t.clone()).collect(); + let func_type = Type::Function(arg_types.clone(), Box::new(return_type.clone())); + self.type_env.insert(name.clone(), func_type); + + let mut func_checker = self.clone(); + for (arg_name, arg_type) in args { + func_checker + .type_env + .insert(arg_name.clone(), arg_type.clone()); + } + + for stmt in body { + let stmt_type = func_checker.check(stmt)?; + if let ASTNode::Return { .. } = stmt { + if stmt_type != *return_type { + return Err(BellronosError::Type(format!( + "Function {} return type mismatch: expected {:?}, found {:?}", + name, return_type, stmt_type + ))); + } + } + } + Ok(Type::None) + } + ASTNode::ClassDef { name, methods } => { + let mut class_methods = HashMap::new(); + for method in methods { + if let ASTNode::FunctionDef { + name: method_name, + args, + return_type, + .. + } = method + { + let arg_types: Vec = args.iter().map(|(_, t)| t.clone()).collect(); + class_methods.insert( + method_name.clone(), + Type::Function(arg_types, Box::new(return_type.clone())), + ); + } + } + self.class_env.insert(name.clone(), class_methods); + self.type_env + .insert(name.clone(), Type::Class(name.clone())); + Ok(Type::None) + } + ASTNode::Assign { target, value } => { + let value_type = self.check(value)?; + self.type_env.insert(target.clone(), value_type); + Ok(Type::None) + } + ASTNode::Expr { value } => self.check(value), + ASTNode::Call { func, args } => { + let func_type = + self.type_env.get(func).cloned().ok_or_else(|| { + BellronosError::Type(format!("Undefined function: {}", func)) + })?; + + if let Type::Function(param_types, return_type) = func_type { + if args.len() != param_types.len() { + return Err(BellronosError::Type(format!( + "Function {} expects {} arguments, but {} were given", + func, + param_types.len(), + args.len() + ))); + } + for (arg, expected_type) in args.iter().zip(param_types.iter()) { + let arg_type = self.check(arg)?; + if !self.is_compatible(&arg_type, expected_type) { + return Err(BellronosError::Type(format!( + "Type mismatch: expected {:?}, found {:?}", + expected_type, arg_type + ))); + } + } + Ok(*return_type) + } else { + Err(BellronosError::Type(format!("{} is not a function", func))) + } + } + ASTNode::Str { .. } => Ok(Type::String), + ASTNode::Num { .. } => Ok(Type::Float), + ASTNode::Bool { .. } => Ok(Type::Bool), + ASTNode::Name { id } => self + .type_env + .get(id) + .cloned() + .ok_or_else(|| BellronosError::Type(format!("Undefined variable: {}", id))), + ASTNode::BinOp { left, op, right } => { + let left_type = self.check(left)?; + let right_type = self.check(right)?; + self.check_binary_op(&left_type, op, &right_type) + } + ASTNode::If { + condition, + body, + orelse, + } => { + let cond_type = self.check(condition)?; + if cond_type != Type::Bool { + return Err(BellronosError::Type( + "If condition must be a boolean".to_string(), + )); + } + for stmt in body { + self.check(stmt)?; + } + for stmt in orelse { + self.check(stmt)?; + } + Ok(Type::None) + } + ASTNode::While { condition, body } => { + let cond_type = self.check(condition)?; + if cond_type != Type::Bool { + return Err(BellronosError::Type( + "While condition must be a boolean".to_string(), + )); + } + for stmt in body { + self.check(stmt)?; + } + Ok(Type::None) + } + ASTNode::For { target, iter, body } => { + let iter_type = self.check(iter)?; + if let Type::List(element_type) = iter_type { + self.type_env.insert(target.clone(), *element_type); + for stmt in body { + self.check(stmt)?; + } + Ok(Type::None) + } else { + Err(BellronosError::Type( + "For loop iterable must be a list".to_string(), + )) + } + } + ASTNode::Return { value } => { + if let Some(v) = value { + self.check(v) + } else { + Ok(Type::None) + } + } + ASTNode::Closure { params, body } => { + let param_types: Vec = params.iter().map(|(_, t)| t.clone()).collect(); + let mut closure_checker = self.clone(); + for (param_name, param_type) in params { + closure_checker + .type_env + .insert(param_name.clone(), param_type.clone()); + } + let return_type = closure_checker.check(body)?; + Ok(Type::Function(param_types, Box::new(return_type))) + } + ASTNode::Generator { body } => { + for stmt in body { + self.check(stmt)?; + } + Ok(Type::List(Box::new(Type::Any))) + } + ASTNode::Yield { value } => self.check(value), + ASTNode::Async { body } => { + for stmt in body { + self.check(stmt)?; + } + Ok(Type::Any) + } + ASTNode::Await { value } => self.check(value), + ASTNode::List { elements } => { + if elements.is_empty() { + Ok(Type::List(Box::new(Type::Any))) + } else { + let first_type = self.check(&elements[0])?; + for element in elements.iter().skip(1) { + let element_type = self.check(element)?; + if !self.is_compatible(&element_type, &first_type) { + return Err(BellronosError::Type( + "All list elements must have compatible types".to_string(), + )); + } + } + Ok(Type::List(Box::new(first_type))) + } + } + ASTNode::Dict { pairs } => { + if pairs.is_empty() { + Ok(Type::Dict(Box::new(Type::Any), Box::new(Type::Any))) + } else { + let (first_key, first_value) = &pairs[0]; + let key_type = self.check(first_key)?; + let value_type = self.check(first_value)?; + for (key, value) in pairs.iter().skip(1) { + let k_type = self.check(key)?; + let v_type = self.check(value)?; + if !self.is_compatible(&k_type, &key_type) + || !self.is_compatible(&v_type, &value_type) + { + return Err(BellronosError::Type( + "All dictionary keys must have compatible types, and all values must have compatible types".to_string(), + )); + } + } + Ok(Type::Dict(Box::new(key_type), Box::new(value_type))) + } + } + ASTNode::Attribute { value, attr } => { + let value_type = self.check(value)?; + if let Type::Instance(class_name) = value_type { + if let Some(class_methods) = self.class_env.get(&class_name) { + class_methods.get(attr).cloned().ok_or_else(|| { + BellronosError::Type(format!( + "Attribute '{}' not found in class '{}'", + attr, class_name + )) + }) + } else { + Err(BellronosError::Type(format!( + "Class '{}' not found", + class_name + ))) + } + } else { + Err(BellronosError::Type(format!( + "Cannot access attribute '{}' on non-instance type {:?}", + attr, value_type + ))) + } + } + ASTNode::InteropCall { language, code } => { + let interop_lang = InteropLanguage::from_str(language)?; + Ok(Type::Interop(interop_lang.infer_type(code)?)) + } + } + } + + fn check_binary_op(&self, left: &Type, op: &str, right: &Type) -> Result { + match (left, op, right) { + (Type::Int, "+", Type::Int) => Ok(Type::Int), + (Type::Float, "+", Type::Float) => Ok(Type::Float), + (Type::String, "+", Type::String) => Ok(Type::String), + (Type::Int, "-", Type::Int) => Ok(Type::Int), + (Type::Float, "-", Type::Float) => Ok(Type::Float), + (Type::Int, "*", Type::Int) => Ok(Type::Int), + (Type::Float, "*", Type::Float) => Ok(Type::Float), + (Type::Int, "/", Type::Int) => Ok(Type::Float), + (Type::Float, "/", Type::Float) => Ok(Type::Float), + (Type::Int | Type::Float, op, Type::Int | Type::Float) + if op == ">" || op == "<" || op == ">=" || op == "<=" => + { + Ok(Type::Bool) + } + (_, "==" | "!=", _) => Ok(Type::Bool), + _ => Err(BellronosError::Type(format!( + "Invalid operation: {:?} {} {:?}", + left, op, right + ))), + } + } + + fn is_compatible(&self, actual: &Type, expected: &Type) -> bool { + match (actual, expected) { + (Type::Any, _) | (_, Type::Any) => true, + (Type::Int, Type::Float) | (Type::Float, Type::Int) => true, + (Type::List(a), Type::List(b)) => self.is_compatible(a, b), + (Type::Dict(ka, va), Type::Dict(kb, vb)) => { + self.is_compatible(ka, kb) && self.is_compatible(va, vb) + } + (Type::Function(params_a, return_a), Type::Function(params_b, return_b)) => { + params_a.len() == params_b.len() + && params_a + .iter() + .zip(params_b.iter()) + .all(|(a, b)| self.is_compatible(a, b)) + && self.is_compatible(return_a, return_b) + } + (Type::Interop(a), Type::Interop(b)) => a == b, + (Type::Interop(_), _) | (_, Type::Interop(_)) => false, // Interop types are only compatible with themselves + (a, b) => a == b, + } + } + + pub fn get_type(&self, name: &str) -> Option<&Type> { + self.type_env.get(name) + } + + pub fn set_type(&mut self, name: String, typ: Type) { + self.type_env.insert(name, typ); + } + + pub fn get_class_method(&self, class_name: &str, method_name: &str) -> Option<&Type> { + self.class_env + .get(class_name) + .and_then(|methods| methods.get(method_name)) + } + + pub fn add_class_method(&mut self, class_name: &str, method_name: String, method_type: Type) { + self.class_env + .entry(class_name.to_string()) + .or_insert_with(HashMap::new) + .insert(method_name, method_type); + } +} + +impl From for Type { + fn from(interop_type: InteropType) -> Self { + match interop_type { + InteropType::CInt | InteropType::PyInt => Type::Int, + InteropType::CFloat | InteropType::PyFloat | InteropType::JsNumber => Type::Float, + InteropType::CString | InteropType::PyString | InteropType::JsString => Type::String, + InteropType::PyList | InteropType::JsArray => Type::List(Box::new(Type::Any)), + InteropType::PyDict | InteropType::JsObject => { + Type::Dict(Box::new(Type::Any), Box::new(Type::Any)) + } + InteropType::JsBoolean => Type::Bool, + InteropType::Unknown => Type::Any, + } + } +} + +pub fn interop_type_to_bellronos_type(interop_type: InteropType) -> Type { + Type::from(interop_type) +} + +pub fn bellronos_type_to_interop_type( + bellronos_type: &Type, + language: &InteropLanguage, +) -> InteropType { + match (bellronos_type, language) { + (Type::Int, InteropLanguage::C) => InteropType::CInt, + (Type::Float, InteropLanguage::C) => InteropType::CFloat, + (Type::String, InteropLanguage::C) => InteropType::CString, + (Type::Int, InteropLanguage::Python) => InteropType::PyInt, + (Type::Float, InteropLanguage::Python) => InteropType::PyFloat, + (Type::String, InteropLanguage::Python) => InteropType::PyString, + (Type::List(_), InteropLanguage::Python) => InteropType::PyList, + (Type::Dict(_, _), InteropLanguage::Python) => InteropType::PyDict, + (Type::Int | Type::Float, InteropLanguage::JavaScript) => InteropType::JsNumber, + (Type::String, InteropLanguage::JavaScript) => InteropType::JsString, + (Type::Bool, InteropLanguage::JavaScript) => InteropType::JsBoolean, + (Type::List(_), InteropLanguage::JavaScript) => InteropType::JsArray, + (Type::Dict(_, _), InteropLanguage::JavaScript) => InteropType::JsObject, + _ => InteropType::Unknown, + } +}