diff --git a/mlf-cli/src/generate/mlf.rs b/mlf-cli/src/generate/mlf.rs index 4f0b818..8709fbf 100644 --- a/mlf-cli/src/generate/mlf.rs +++ b/mlf-cli/src/generate/mlf.rs @@ -1,12 +1,13 @@ use miette::Diagnostic; use serde_json::Value; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug, Diagnostic)] pub enum MlfGenerateError { #[error("Failed to read file: {path}")] #[diagnostic(code(mlf::generate::read_file))] + #[allow(dead_code)] ReadFile { path: String, #[source] @@ -15,6 +16,7 @@ pub enum MlfGenerateError { #[error("Failed to parse JSON: {path}")] #[diagnostic(code(mlf::generate::parse_json))] + #[allow(dead_code)] ParseJson { path: String, #[source] @@ -161,6 +163,16 @@ pub fn run(input_patterns: Vec, output_dir: PathBuf) -> Result<(), MlfGe pub fn generate_mlf_from_json(json: &Value) -> Result { let mut output = String::new(); + // Extract NSID to get the last segment for "main" definitions + let nsid = json + .get("id") + .and_then(|v| v.as_str()) + .ok_or_else(|| MlfGenerateError::InvalidLexicon { + message: "Missing 'id' field in lexicon".to_string(), + })?; + + let last_segment = nsid.split('.').last().unwrap_or("main"); + let defs = json.get("defs").and_then(|v| v.as_object()).ok_or_else(|| { MlfGenerateError::InvalidLexicon { message: "Missing or invalid 'defs' field".to_string(), @@ -177,22 +189,22 @@ pub fn generate_mlf_from_json(json: &Value) -> Result match def_type { "record" => { - let mlf = generate_record(name, def)?; + let mlf = generate_record(name, def, last_segment)?; output.push_str(&mlf); output.push('\n'); } "query" => { - let mlf = generate_query(name, def)?; + let mlf = generate_query(name, def, last_segment)?; output.push_str(&mlf); output.push('\n'); } "procedure" => { - let mlf = generate_procedure(name, def)?; + let mlf = generate_procedure(name, def, last_segment)?; output.push_str(&mlf); output.push('\n'); } "subscription" => { - let mlf = generate_subscription(name, def)?; + let mlf = generate_subscription(name, def, last_segment)?; output.push_str(&mlf); output.push('\n'); } @@ -215,7 +227,23 @@ pub fn generate_mlf_from_json(json: &Value) -> Result Ok(output) } -fn generate_record(name: &str, def: &Value) -> Result { +/// Reserved words in MLF that need to be escaped +const RESERVED_WORDS: &[&str] = &[ + "main", "record", "query", "procedure", "subscription", "token", "def", "type", "use", + "pub", "alias", "namespace", "constrained", "error", "unit", "null", "boolean", + "integer", "string", "bytes", "blob", "unknown", "array", "object", "union", "ref", +]; + +/// Escape a name if it's a reserved word +fn escape_name(name: &str) -> String { + if RESERVED_WORDS.contains(&name) { + format!("`{}`", name) + } else { + name.to_string() + } +} + +fn generate_record(name: &str, def: &Value, last_segment: &str) -> Result { let mut output = String::new(); // Add doc comment if present @@ -227,13 +255,11 @@ fn generate_record(name: &str, def: &Value) -> Result } } - // Use "main" name if present, otherwise use the definition name + // Use last segment of NSID for "main" definitions let record_name = if name == "main" { - // Try to extract the last segment from the namespace ID - // This is a heuristic - we could make it better - "main" + escape_name(last_segment) } else { - name + escape_name(name) }; output.push_str(&format!("record {} {{\n", record_name)); @@ -286,7 +312,7 @@ fn generate_record(name: &str, def: &Value) -> Result Ok(output) } -fn generate_query(name: &str, def: &Value) -> Result { +fn generate_query(name: &str, def: &Value, last_segment: &str) -> Result { let mut output = String::new(); // Add doc comment @@ -298,7 +324,11 @@ fn generate_query(name: &str, def: &Value) -> Result { } } - let query_name = if name == "main" { "query" } else { name }; + let query_name = if name == "main" { + escape_name(last_segment) + } else { + escape_name(name) + }; output.push_str(&format!("query {}", query_name)); // Parameters @@ -368,7 +398,7 @@ fn generate_query(name: &str, def: &Value) -> Result { Ok(output) } -fn generate_procedure(name: &str, def: &Value) -> Result { +fn generate_procedure(name: &str, def: &Value, last_segment: &str) -> Result { let mut output = String::new(); // Add doc comment @@ -380,7 +410,11 @@ fn generate_procedure(name: &str, def: &Value) -> Result Result Result { +fn generate_subscription(name: &str, def: &Value, last_segment: &str) -> Result { let mut output = String::new(); // Add doc comment @@ -469,9 +503,9 @@ fn generate_subscription(name: &str, def: &Value) -> Result bool { + fn has_module(&self, _namespace: &str) -> bool { // This requires exposing the modules field or adding a method to mlf-lang // For now, we'll just try to add and catch the error // TODO: Add a proper has_module method to Workspace