pub mod token;
pub mod lexer;
pub mod parser;
pub mod interpreter;
pub mod value;
use std::fs;
use std::path::Path;
use self::parser::{Expr, Stmt};
pub fn run_file(path: &str) -> Result<(), String> {
let source = fs::read_to_string(path)
.map_err(|e| format!("Failed to read '{}': {}", path, e))?;
let base = Path::new(path).parent().unwrap_or(Path::new("."));
let file_name = Path::new(path).file_name().unwrap_or_default().to_str().unwrap_or("");
run_with_base(&source, base.to_str().unwrap_or("."), Some(file_name))
}
pub fn run(source: &str) -> Result<(), String> {
run_with_base(source, ".", None)
}
pub fn run_with_base(source: &str, base_path: &str, entry_file: Option<&str>) -> Result<(), String> {
if source_declares_foundation(source) {
validate_foundation_source(source)?;
}
let tokens = lexer::tokenize(source)?;
let ast = parser::parse(&tokens)?;
if ast_declares_foundation(&ast) {
validate_foundation_source(source)?;
validate_foundation_ast(&ast)?;
}
let mut interp = interpreter::Interpreter::new().with_base_path(base_path);
if let Some(file) = entry_file {
interp.mark_imported(file);
}
interp.execute(&ast)?;
interp.invoke_main_if_present()?;
Ok(())
}
fn ast_declares_foundation(stmts: &[Stmt]) -> bool {
stmts.iter().any(|stmt| match stmt {
Stmt::SystemIris { traits } => traits.iter().any(|trait_name| {
trait_name.eq_ignore_ascii_case("foundation")
}),
_ => false,
})
}
fn source_declares_foundation(source: &str) -> bool {
source.lines().any(|line| {
let code = line.split("//").next().unwrap_or("").to_ascii_lowercase();
code.contains("system") && code.contains("iris") && code.contains("foundation")
})
}
fn validate_foundation_source(source: &str) -> Result<(), String> {
for (line_index, line) in source.lines().enumerate() {
let code = line.split("//").next().unwrap_or("");
let compact = code.split_whitespace().collect::<String>();
if compact.contains("#[no_std]") {
return Err(foundation_error(
"#[no_std]",
line_index + 1,
code.find("#[no_std]").map(|i| i + 1).unwrap_or(1),
));
}
let lowercase = code.to_ascii_lowercase();
if let Some(column) = lowercase.find("using namespace std") {
return Err(foundation_error(
"using namespace std",
line_index + 1,
column + 1,
));
}
}
Ok(())
}
fn validate_foundation_ast(stmts: &[Stmt]) -> Result<(), String> {
for stmt in stmts {
validate_foundation_stmt(stmt)?;
}
Ok(())
}
fn validate_foundation_stmt(stmt: &Stmt) -> Result<(), String> {
match stmt {
Stmt::Expr(expr) => validate_foundation_expr(expr),
Stmt::Let { value, .. } | Stmt::Const { value, .. } => validate_foundation_expr(value),
Stmt::Fn { body, .. } | Stmt::Block(body) | Stmt::While { body, .. } | Stmt::For { body, .. } => {
for stmt in body {
validate_foundation_stmt(stmt)?;
}
Ok(())
}
Stmt::If { condition, then_branch, else_branch } => {
validate_foundation_expr(condition)?;
for stmt in then_branch {
validate_foundation_stmt(stmt)?;
}
if let Some(stmts) = else_branch {
for stmt in stmts {
validate_foundation_stmt(stmt)?;
}
}
Ok(())
}
Stmt::Return(Some(expr)) => validate_foundation_expr(expr),
Stmt::Return(None) | Stmt::Break | Stmt::Continue | Stmt::Import { .. } | Stmt::SystemIris { .. } => Ok(()),
}
}
fn validate_foundation_expr(expr: &Expr) -> Result<(), String> {
match expr {
Expr::Call { callee, args } => {
if let Expr::Identifier(name) = callee.as_ref() {
if is_std_builtin(name) {
return Err(foundation_std_required_error(name));
}
}
validate_foundation_expr(callee)?;
for arg in args {
validate_foundation_expr(arg)?;
}
Ok(())
}
Expr::Array(elements) => {
for expr in elements {
validate_foundation_expr(expr)?;
}
Ok(())
}
Expr::Object(pairs) => {
for (_, expr) in pairs {
validate_foundation_expr(expr)?;
}
Ok(())
}
Expr::Binary { left, right, .. } => {
validate_foundation_expr(left)?;
validate_foundation_expr(right)
}
Expr::Unary { expr, .. } => validate_foundation_expr(expr),
Expr::Index { object, index } => {
validate_foundation_expr(object)?;
validate_foundation_expr(index)
}
Expr::Member { object, .. } => validate_foundation_expr(object),
Expr::Assign { target, value } => {
validate_foundation_expr(target)?;
validate_foundation_expr(value)
}
Expr::Lambda { body, .. } => {
for stmt in body {
validate_foundation_stmt(stmt)?;
}
Ok(())
}
Expr::Null | Expr::Bool(_) | Expr::Number(_) | Expr::String(_) | Expr::Identifier(_) => Ok(()),
}
}
fn is_std_builtin(name: &str) -> bool {
matches!(
name,
"print"
| "println"
| "input"
| "len"
| "typeof"
| "push"
| "pop"
| "keys"
| "values"
| "range"
| "str"
| "num"
| "system"
| "fs_read"
| "fs_write"
| "env_get"
| "sleep"
| "proc_list"
)
}
fn foundation_error(rule: &str, line: usize, column: usize) -> String {
format!(
"\x1b[31mFoundation error: '{}' is not allowed in Iris Foundation mode at line {}, column {}. Iris Foundation owns std access, memory safety, ownership, borrow-checkers, and leak safety.\x1b[0m",
rule, line, column
)
}
fn foundation_std_required_error(name: &str) -> String {
format!(
"\x1b[31mFoundation error: Iris Foundation cannot run standard runtime calls without the std namespace. Use std::{}(...) instead of {}(...).\x1b[0m",
name, name
)
}
pub fn repl() {
use std::io::{self, Write};
println!("Iris REPL v0.1.0");
println!("Type 'exit' or press Ctrl+C to quit.\n");
let mut interp = interpreter::Interpreter::new();
loop {
print!("iris> ");
io::stdout().flush().unwrap();
let mut input = String::new();
if io::stdin().read_line(&mut input).is_err() {
break;
}
let input = input.trim();
if input == "exit" || input == "quit" {
break;
}
if input.is_empty() {
continue;
}
match lexer::tokenize(input) {
Ok(tokens) => {
match parser::parse(&tokens) {
Ok(ast) => {
match interp.execute(&ast) {
Ok(result) => {
if !matches!(result, value::Value::Null) {
println!("{}", result);
}
}
Err(e) => eprintln!("Runtime error: {}", e),
}
}
Err(e) => eprintln!("Parse error: {}", e),
}
}
Err(e) => eprintln!("Lex error: {}", e),
}
}
}