neutron-engine 0.1.3

A lightweight markup parser for the Neutron styling language (.nt)
Documentation
/// Iris Programming Language - Interpreter Module
/// 
/// Iris adalah bahasa scripting dengan paradigm hybrid:
/// - Object-oriented (prototype-based seperti JavaScript)
/// - Imperative (control flow seperti C/Rust)
/// - Functional (first-class functions, closures)

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};

/// Run an Iris source file
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))
}

/// Run Iris source code directly
pub fn run(source: &str) -> Result<(), String> {
    run_with_base(source, ".", None)
}

/// Run with base path for imports
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
    )
}

/// REPL - Read Eval Print Loop
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),
        }
    }
}