latest pushes
This commit is contained in:
parent
4426007efa
commit
62363b8b23
22
.github/workflows/rust.yml
vendored
Normal file
22
.github/workflows/rust.yml
vendored
Normal file
@ -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
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
publish.sh
|
||||
Cargo.lock
|
||||
target
|
||||
scripts
|
64
Cargo.toml
Normal file
64
Cargo.toml
Normal file
@ -0,0 +1,64 @@
|
||||
[package]
|
||||
name = "bellronos"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
authors = ["Ronaldson Bellande <ronaldsonbellande@gmail.com>"]
|
||||
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"]
|
18
README.md
18
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.
|
||||
|
100
bellronos_file/hello_world.bellronos
Normal file
100
bellronos_file/hello_world.bellronos
Normal file
@ -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 <stdio.h>
|
||||
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()
|
22
dependencies.txt
Normal file
22
dependencies.txt
Normal file
@ -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"
|
3
git_scripts/.gitignore
vendored
Normal file
3
git_scripts/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
fix_errors.sh
|
||||
push.sh
|
||||
repository_recal.sh
|
1
make_rust_executable.bellos
Executable file
1
make_rust_executable.bellos
Executable file
@ -0,0 +1 @@
|
||||
bellande_rust_executable -d dependencies.txt -s src -m bellronos.rs -o executable/bellronos
|
1
make_rust_executable.sh
Executable file
1
make_rust_executable.sh
Executable file
@ -0,0 +1 @@
|
||||
bellande_rust_executable -d dependencies.txt -s src -m bellronos.rs -o executable/bellronos
|
111
src/ast/ast.rs
Normal file
111
src/ast/ast.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::type_system::type_system::Type;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum ASTNode {
|
||||
Module {
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
Import {
|
||||
names: Vec<String>,
|
||||
},
|
||||
FunctionDef {
|
||||
name: String,
|
||||
args: Vec<(String, Type)>,
|
||||
return_type: Type,
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
ClassDef {
|
||||
name: String,
|
||||
methods: Vec<ASTNode>,
|
||||
},
|
||||
Assign {
|
||||
target: String,
|
||||
value: Box<ASTNode>,
|
||||
},
|
||||
Expr {
|
||||
value: Box<ASTNode>,
|
||||
},
|
||||
Call {
|
||||
func: String,
|
||||
args: Vec<ASTNode>,
|
||||
},
|
||||
Str {
|
||||
value: String,
|
||||
},
|
||||
Num {
|
||||
value: f64,
|
||||
},
|
||||
Bool {
|
||||
value: bool,
|
||||
},
|
||||
Name {
|
||||
id: String,
|
||||
},
|
||||
BinOp {
|
||||
left: Box<ASTNode>,
|
||||
op: String,
|
||||
right: Box<ASTNode>,
|
||||
},
|
||||
If {
|
||||
condition: Box<ASTNode>,
|
||||
body: Vec<ASTNode>,
|
||||
orelse: Vec<ASTNode>,
|
||||
},
|
||||
While {
|
||||
condition: Box<ASTNode>,
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
For {
|
||||
target: String,
|
||||
iter: Box<ASTNode>,
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
Return {
|
||||
value: Option<Box<ASTNode>>,
|
||||
},
|
||||
Closure {
|
||||
params: Vec<(String, Type)>,
|
||||
body: Box<ASTNode>,
|
||||
},
|
||||
Generator {
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
Yield {
|
||||
value: Box<ASTNode>,
|
||||
},
|
||||
Async {
|
||||
body: Vec<ASTNode>,
|
||||
},
|
||||
Await {
|
||||
value: Box<ASTNode>,
|
||||
},
|
||||
List {
|
||||
elements: Vec<ASTNode>,
|
||||
},
|
||||
Dict {
|
||||
pairs: Vec<(ASTNode, ASTNode)>,
|
||||
},
|
||||
Attribute {
|
||||
value: Box<ASTNode>,
|
||||
attr: String,
|
||||
},
|
||||
InteropCall {
|
||||
language: String,
|
||||
code: String,
|
||||
},
|
||||
}
|
1
src/ast/mod.rs
Normal file
1
src/ast/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod ast;
|
53
src/bellronos.rs
Normal file
53
src/bellronos.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
println!("Usage: bellronos <filename> [--install <package>]");
|
||||
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(())
|
||||
}
|
49
src/error/error.rs
Normal file
49
src/error/error.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<io::Error> for BellronosError {
|
||||
fn from(err: io::Error) -> BellronosError {
|
||||
BellronosError::IO(err)
|
||||
}
|
||||
}
|
1
src/error/mod.rs
Normal file
1
src/error/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod error;
|
147
src/interop/interop.rs
Normal file
147
src/interop/interop.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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)
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
1
src/interop/mod.rs
Normal file
1
src/interop/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod interop;
|
450
src/interpreter/interpreter.rs
Normal file
450
src/interpreter/interpreter.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<RefCell<HashMap<String, Value>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
String(String),
|
||||
Bool(bool),
|
||||
List(Vec<Value>),
|
||||
Dict(HashMap<String, Value>),
|
||||
Function(Vec<String>, Vec<ASTNode>, Environment),
|
||||
Class {
|
||||
methods: HashMap<String, Value>,
|
||||
},
|
||||
Instance {
|
||||
class: String,
|
||||
attributes: HashMap<String, Value>,
|
||||
},
|
||||
Closure(Vec<String>, Vec<ASTNode>, Environment),
|
||||
Generator(Vec<ASTNode>, 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<H: Hasher>(&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<Value, BellronosError> {
|
||||
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<Value, BellronosError> {
|
||||
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, "<function>"),
|
||||
Value::Class { .. } => write!(f, "<class>"),
|
||||
Value::Instance { class, .. } => write!(f, "<instance of {}>", class),
|
||||
Value::Closure(_, _, _) => write!(f, "<closure>"),
|
||||
Value::Generator(_, _, _) => write!(f, "<generator>"),
|
||||
Value::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
1
src/interpreter/mod.rs
Normal file
1
src/interpreter/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod interpreter;
|
308
src/lexer/lexer.rs
Normal file
308
src/lexer/lexer.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<char>,
|
||||
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<Vec<Token>, 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<Option<Token>, 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<Option<Token>, 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<Option<Token>, 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::<f64>()
|
||||
.map(|n| Some(Token::Number(n)))
|
||||
.map_err(|_| {
|
||||
BellronosError::Parser(format!("Unexpected character: {}", self.current_char()))
|
||||
})
|
||||
}
|
||||
|
||||
fn tokenize_identifier(&mut self) -> Result<Option<Token>, 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();
|
||||
}
|
||||
}
|
||||
}
|
1
src/lexer/mod.rs
Normal file
1
src/lexer/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod lexer;
|
1
src/package_manager/mod.rs
Normal file
1
src/package_manager/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod package_manager;
|
250
src/package_manager/package_manager.rs
Normal file
250
src/package_manager/package_manager.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<String>,
|
||||
}
|
||||
|
||||
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<String, BellronosError> {
|
||||
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<Vec<String>, 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<PackageMetadata, BellronosError> {
|
||||
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::<PackageMetadata>()
|
||||
.map_err(|e| BellronosError::Parser(format!("Failed to parse package metadata: {}", e)))
|
||||
}
|
||||
|
||||
fn download_package(&self, metadata: &PackageMetadata) -> Result<String, BellronosError> {
|
||||
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<String, BellronosError> {
|
||||
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<Vec<String>, 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::<Vec<String>>()
|
||||
.map_err(|e| BellronosError::Parser(format!("Failed to parse search results: {}", e)))
|
||||
}
|
||||
|
||||
pub fn get_package_info(&self, package_name: &str) -> Result<String, BellronosError> {
|
||||
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)))
|
||||
}
|
||||
}
|
1
src/parser/mod.rs
Normal file
1
src/parser/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod parser;
|
455
src/parser/parser.rs
Normal file
455
src/parser/parser.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<Token>,
|
||||
position: usize,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<Token>) -> Self {
|
||||
Parser {
|
||||
tokens,
|
||||
position: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
self.parse_binary_operation()
|
||||
}
|
||||
|
||||
fn parse_binary_operation(&mut self) -> Result<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<ASTNode, BellronosError> {
|
||||
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<Vec<(String, Type)>, 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<Type, BellronosError> {
|
||||
if self.current_token() == Token::Arrow {
|
||||
self.advance();
|
||||
self.parse_type()
|
||||
} else {
|
||||
Ok(Type::None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_type(&mut self) -> Result<Type, BellronosError> {
|
||||
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<Vec<ASTNode>, 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<String, BellronosError> {
|
||||
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)
|
||||
}
|
||||
}
|
1
src/standard_library/mod.rs
Normal file
1
src/standard_library/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod standard_library;
|
102
src/standard_library/standard_library.rs
Normal file
102
src/standard_library/standard_library.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::interpreter::interpreter::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct StandardLibrary {
|
||||
modules: HashMap<String, HashMap<String, Value>>,
|
||||
}
|
||||
|
||||
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<String, Value>> {
|
||||
self.modules.get(name)
|
||||
}
|
||||
}
|
1
src/type_system/mod.rs
Normal file
1
src/type_system/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod type_system;
|
507
src/type_system/type_system.rs
Normal file
507
src/type_system/type_system.rs
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<Self, Self::Err> {
|
||||
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<InteropType, BellronosError> {
|
||||
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<Type>),
|
||||
Dict(Box<Type>, Box<Type>),
|
||||
Function(Vec<Type>, Box<Type>),
|
||||
Class(String),
|
||||
Instance(String),
|
||||
None,
|
||||
Any,
|
||||
Custom(String),
|
||||
Interop(InteropType),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeChecker {
|
||||
type_env: HashMap<String, Type>,
|
||||
class_env: HashMap<String, HashMap<String, Type>>,
|
||||
}
|
||||
|
||||
impl TypeChecker {
|
||||
pub fn new() -> Self {
|
||||
TypeChecker {
|
||||
type_env: HashMap::new(),
|
||||
class_env: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(&mut self, node: &ASTNode) -> Result<Type, BellronosError> {
|
||||
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<Type> = 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<Type> = 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<Type> = 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<Type, BellronosError> {
|
||||
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<InteropType> 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,
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user