From b5cafe1a4ae3992ba1fd7018c0f3e9bd3a1683f2 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Tue, 27 Jan 2026 10:55:10 +0000 Subject: [PATCH] pr01 --- Cargo.lock | 14 +++ crates/prometeu-compiler/Cargo.toml | 3 + crates/prometeu-compiler/src/common/config.rs | 43 +++++++ crates/prometeu-compiler/src/common/mod.rs | 1 + crates/prometeu-compiler/src/compiler.rs | 108 +++++++++++++----- crates/prometeu-compiler/src/lib.rs | 10 +- .../tests/config_integration.rs | 36 ++++++ 7 files changed, 181 insertions(+), 34 deletions(-) create mode 100644 crates/prometeu-compiler/src/common/config.rs create mode 100644 crates/prometeu-compiler/tests/config_integration.rs diff --git a/Cargo.lock b/Cargo.lock index b5da6ce2..8d3e3796 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1923,6 +1923,7 @@ dependencies = [ "prometeu-core", "serde", "serde_json", + "tempfile", ] [[package]] @@ -2320,6 +2321,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.1.3", + "windows-sys 0.61.2", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/crates/prometeu-compiler/Cargo.toml b/crates/prometeu-compiler/Cargo.toml index 4e09909d..89d8ff1c 100644 --- a/crates/prometeu-compiler/Cargo.toml +++ b/crates/prometeu-compiler/Cargo.toml @@ -26,3 +26,6 @@ clap = { version = "4.5.54", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" anyhow = "1.0.100" + +[dev-dependencies] +tempfile = "3.10.1" diff --git a/crates/prometeu-compiler/src/common/config.rs b/crates/prometeu-compiler/src/common/config.rs new file mode 100644 index 00000000..670598dc --- /dev/null +++ b/crates/prometeu-compiler/src/common/config.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; +use anyhow::Result; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ProjectConfig { + pub script_fe: String, + pub entry: PathBuf, +} + +impl ProjectConfig { + pub fn load(project_dir: &Path) -> Result { + let config_path = project_dir.join("prometeu.json"); + let content = std::fs::read_to_string(config_path)?; + let config: ProjectConfig = serde_json::from_str(&content)?; + Ok(config) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use tempfile::tempdir; + + #[test] + fn test_load_valid_config() { + let dir = tempdir().unwrap(); + let config_path = dir.path().join("prometeu.json"); + fs::write( + config_path, + r#"{ + "script_fe": "pbs", + "entry": "main.pbs" + }"#, + ) + .unwrap(); + + let config = ProjectConfig::load(dir.path()).unwrap(); + assert_eq!(config.script_fe, "pbs"); + assert_eq!(config.entry, PathBuf::from("main.pbs")); + } +} diff --git a/crates/prometeu-compiler/src/common/mod.rs b/crates/prometeu-compiler/src/common/mod.rs index 5c28ac21..350a49e7 100644 --- a/crates/prometeu-compiler/src/common/mod.rs +++ b/crates/prometeu-compiler/src/common/mod.rs @@ -2,3 +2,4 @@ pub mod diagnostics; pub mod spans; pub mod files; pub mod symbols; +pub mod config; diff --git a/crates/prometeu-compiler/src/compiler.rs b/crates/prometeu-compiler/src/compiler.rs index 560368eb..87feaf3c 100644 --- a/crates/prometeu-compiler/src/compiler.rs +++ b/crates/prometeu-compiler/src/compiler.rs @@ -4,6 +4,7 @@ //! It handles the transition between different compiler phases: Frontend -> IR -> Backend. use crate::backend; +use crate::common::config::ProjectConfig; use crate::common::files::FileManager; use crate::common::symbols::Symbol; use crate::frontends::Frontend; @@ -13,6 +14,7 @@ use std::path::Path; /// The result of a successful compilation process. /// It contains the final binary and the metadata needed for debugging. +#[derive(Debug)] pub struct CompilationUnit { /// The raw binary data formatted as Prometeu ByteCode (PBC). /// This is what gets written to a `.pbc` file. @@ -51,35 +53,87 @@ impl CompilationUnit { /// # Example /// ```no_run /// use std::path::Path; -/// let entry = Path::new("src/main.ts"); -/// let unit = prometeu_compiler::compiler::compile(entry).expect("Failed to compile"); +/// let project_dir = Path::new("."); +/// let unit = prometeu_compiler::compiler::compile(project_dir).expect("Failed to compile"); /// unit.export(Path::new("build/program.pbc"), true, true).unwrap(); /// ``` -pub fn compile(entry: &Path) -> Result { - let mut file_manager = FileManager::new(); +pub fn compile(project_dir: &Path) -> Result { + let config = ProjectConfig::load(project_dir)?; + + // 1. Select Frontend + // The _frontend is responsible for parsing source code and producing the IR. + let _frontend: Box = match config.script_fe.as_str() { + "pbs" => anyhow::bail!("Frontend 'pbs' not yet implemented"), + _ => anyhow::bail!("Invalid frontend: {}", config.script_fe), + }; - // 1. Select Frontend (Currently only TS is supported) - // The frontend is responsible for parsing source code and producing the IR. - let frontend = /** ??? **/; - - // 2. Compile to IR (Intermediate Representation) - // This step abstracts away source-specific syntax (like TypeScript) into a - // generic set of instructions that the backend can understand. - let ir_module = frontend.compile_to_ir(entry, &mut file_manager) - .map_err(|bundle| anyhow::anyhow!("Compilation failed with {} errors", bundle.diagnostics.len()))?; + #[allow(unreachable_code, unused_variables, unused_mut)] + { + let entry = project_dir.join(&config.entry); + let mut file_manager = FileManager::new(); + + // 2. Compile to IR (Intermediate Representation) + // This step abstracts away source-specific syntax (like TypeScript) into a + // generic set of instructions that the backend can understand. + let ir_module = _frontend.compile_to_ir(&entry, &mut file_manager) + .map_err(|bundle| anyhow::anyhow!("Compilation failed with {} errors", bundle.diagnostics.len()))?; + + // 3. IR Validation + // Ensures the generated IR is sound and doesn't violate any VM constraints + // before we spend time generating bytecode. + ir::validate::validate_module(&ir_module) + .map_err(|bundle| anyhow::anyhow!("IR Validation failed: {:?}", bundle))?; + + // 4. Emit Bytecode + // The backend takes the validated IR and produces the final binary executable. + let result = backend::emit_module(&ir_module, &file_manager)?; - // 3. IR Validation - // Ensures the generated IR is sound and doesn't violate any VM constraints - // before we spend time generating bytecode. - ir::validate::validate_module(&ir_module) - .map_err(|bundle| anyhow::anyhow!("IR Validation failed: {:?}", bundle))?; - - // 4. Emit Bytecode - // The backend takes the validated IR and produces the final binary executable. - let result = backend::emit_module(&ir_module, &file_manager)?; - - Ok(CompilationUnit { - rom: result.rom, - symbols: result.symbols, - }) + Ok(CompilationUnit { + rom: result.rom, + symbols: result.symbols, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use tempfile::tempdir; + + #[test] + fn test_invalid_frontend() { + let dir = tempdir().unwrap(); + let config_path = dir.path().join("prometeu.json"); + fs::write( + config_path, + r#"{ + "script_fe": "invalid", + "entry": "main.pbs" + }"#, + ) + .unwrap(); + + let result = compile(dir.path()); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("Invalid frontend: invalid")); + } + + #[test] + fn test_frontend_pbs_not_implemented() { + let dir = tempdir().unwrap(); + let config_path = dir.path().join("prometeu.json"); + fs::write( + config_path, + r#"{ + "script_fe": "pbs", + "entry": "main.pbs" + }"#, + ) + .unwrap(); + + let result = compile(dir.path()); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("Frontend 'pbs' not yet implemented")); + } } diff --git a/crates/prometeu-compiler/src/lib.rs b/crates/prometeu-compiler/src/lib.rs index e5839697..d9e9d2ac 100644 --- a/crates/prometeu-compiler/src/lib.rs +++ b/crates/prometeu-compiler/src/lib.rs @@ -96,12 +96,11 @@ pub fn run() -> Result<()> { match cli.command { Commands::Build { project_dir, - entry, out, emit_disasm, emit_symbols, + .. } => { - let entry = entry.unwrap_or_else(|| project_dir.join("src/main.ts")); let build_dir = project_dir.join("build"); let out = out.unwrap_or_else(|| build_dir.join("program.pbc")); @@ -110,18 +109,15 @@ pub fn run() -> Result<()> { } println!("Building project at {:?}", project_dir); - println!("Entry: {:?}", entry); println!("Output: {:?}", out); - let compilation_unit = compiler::compile(&entry)?; + let compilation_unit = compiler::compile(&project_dir)?; compilation_unit.export(&out, emit_disasm, emit_symbols)?; } Commands::Verify { project_dir } => { - let entry = project_dir.join("src/main.ts"); println!("Verifying project at {:?}", project_dir); - println!("Entry: {:?}", entry); - compiler::compile(&entry)?; + compiler::compile(&project_dir)?; println!("Project is valid!"); } } diff --git a/crates/prometeu-compiler/tests/config_integration.rs b/crates/prometeu-compiler/tests/config_integration.rs new file mode 100644 index 00000000..dea779bd --- /dev/null +++ b/crates/prometeu-compiler/tests/config_integration.rs @@ -0,0 +1,36 @@ +use std::fs; +use tempfile::tempdir; +use prometeu_compiler::compiler; + +#[test] +fn test_project_root_and_entry_resolution() { + let dir = tempdir().unwrap(); + let project_dir = dir.path(); + + // Create prometeu.json + fs::write( + project_dir.join("prometeu.json"), + r#"{ + "script_fe": "pbs", + "entry": "src/main.pbs" + }"#, + ).unwrap(); + + // Create src directory and main.pbs + fs::create_dir(project_dir.join("src")).unwrap(); + fs::write(project_dir.join("src/main.pbs"), "").unwrap(); + + // Call compile + let result = compiler::compile(project_dir); + + // It should fail with "Frontend 'pbs' not yet implemented" + // but ONLY after successfully loading the config and resolving the entry. + + match result { + Err(e) => { + let msg = e.to_string(); + assert!(msg.contains("Frontend 'pbs' not yet implemented"), "Unexpected error: {}", msg); + } + Ok(_) => panic!("Should have failed as pbs is not implemented yet"), + } +}