diff --git a/Cargo.lock b/Cargo.lock index 86252ec..d67b7cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytes" version = "1.11.1" @@ -493,6 +499,7 @@ dependencies = [ name = "mist-codegen" version = "0.3.3-alpha.0" dependencies = [ + "bytecount", "mist-parser", ] diff --git a/crates/analyzer/src/builder.rs b/crates/analyzer/src/builder.rs index 2522602..7f8ecfc 100644 --- a/crates/analyzer/src/builder.rs +++ b/crates/analyzer/src/builder.rs @@ -6,7 +6,7 @@ use std::{ }; use cargo_metadata::{CompilerMessage, Message}; -use mist_parser::rev_mapper::{RustMap, find_mapping, get_mapping}; +use mist_parser::rev_mapper; #[derive(Debug, Clone)] pub struct MistDiagnosticMessage { @@ -39,7 +39,7 @@ pub fn build(mut args: Vec, root: PathBuf) -> Vec { let mut diagnostics = Vec::new(); - let mut mapping = HashMap::new(); + let mut mapping: HashMap = HashMap::new(); let mist_src = format!(".mist{MAIN_SEPARATOR}lsp"); @@ -64,44 +64,44 @@ pub fn build(mut args: Vec, root: PathBuf) -> Vec { break; } - let map = mapping.entry(rust_path.clone()).or_insert_with(|| { - get_mapping( - &fs::read_to_string(rust_path) - .expect("Failed to read file for mapping"), - ) - }); + // let map = mapping.entry(rust_path.clone()).or_insert_with(|| { + // get_mapping( + // &fs::read_to_string(rust_path) + // .expect("Failed to read file for mapping"), + // ) + // }); - let mist_span = - find_mapping(&map, &RustMap(span.line_end, span.column_start)) - .expect("Unable to find mapping"); + // let mist_span = + // find_mapping(&map, &RustMap(span.line_end, span.column_start)) + // .expect("Unable to find mapping"); - let mist_msg = MistDiagnosticMessage { - message: format!( - "{}: {}", - msg.message.message, - span.label.clone().unwrap_or_default() - ), - file_name: if is_root { - mist_file - } else { - mist_path.to_string_lossy().to_string() - }, - file_path: mist_path, - line: mist_span.1.0, - column: mist_span.1.1, - }; + // let mist_msg = MistDiagnosticMessage { + // message: format!( + // "{}: {}", + // msg.message.message, + // span.label.clone().unwrap_or_default() + // ), + // file_name: if is_root { + // mist_file + // } else { + // mist_path.to_string_lossy().to_string() + // }, + // file_path: mist_path, + // line: mist_span.1.0, + // column: mist_span.1.1, + // }; - match msg.message.level { - cargo_metadata::diagnostic::DiagnosticLevel::Error => { - diagnostics.push(MistDiagnostic::Error(mist_msg)) - } + // match msg.message.level { + // cargo_metadata::diagnostic::DiagnosticLevel::Error => { + // diagnostics.push(MistDiagnostic::Error(mist_msg)) + // } - cargo_metadata::diagnostic::DiagnosticLevel::Warning => { - diagnostics.push(MistDiagnostic::Warning(mist_msg)) - } + // cargo_metadata::diagnostic::DiagnosticLevel::Warning => { + // diagnostics.push(MistDiagnostic::Warning(mist_msg)) + // } - _ => {} - } + // _ => {} + // } } } } diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 366eb5d..5569ad2 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -57,29 +57,30 @@ impl Backend { match self.mapping.lock().await.get(&rs_path) { Some(mapping) => { + rs_loc // FIX: Match the find_mapping operation instead of calling .expect("Failed to map") - match rev_mapper::find_mapping( - mapping, - &rev_mapper::RustMap( - rs_loc.range.start.line as usize, - rs_loc.range.start.character as usize, - ), - ) { - Some((_, rev_mapper::MistMap(line, character))) => Location { - uri: Url::from_file_path(from_rust_to_mist(rs_path)).unwrap(), - range: Range { - start: Position { - line: line as u32 - 1, - character: character as u32, - }, - end: Position { - line: line as u32 - 1, - character: character as u32 + 1, - }, - }, - }, - None => rs_loc, - } + // match rev_mapper::find_mapping( + // mapping, + // &rev_mapper::RustMap( + // rs_loc.range.start.line as usize, + // rs_loc.range.start.character as usize, + // ), + // ) { + // Some((_, rev_mapper::MistMap(line, character))) => Location { + // uri: Url::from_file_path(from_rust_to_mist(rs_path)).unwrap(), + // range: Range { + // start: Position { + // line: line as u32 - 1, + // character: character as u32, + // }, + // end: Position { + // line: line as u32 - 1, + // character: character as u32 + 1, + // }, + // }, + // }, + // None => rs_loc, + // } } None => rs_loc, } @@ -167,10 +168,10 @@ impl LanguageServer for Backend { .insert(file.clone(), Rope::from_str(&text)); // store mapping - mapping - .lock() - .await - .insert(rust_path, rev_mapper::get_mapping(&transpiled)); + // mapping + // .lock() + // .await + // .insert(rust_path, rev_mapper::get_mapping(&transpiled)); } } } @@ -311,10 +312,10 @@ impl LanguageServer for Backend { params.text_document.uri = Url::from_file_path(&rust_path).unwrap(); - self.mapping.lock().await.insert( - rust_path, - rev_mapper::get_mapping(¶ms.text_document.text), - ); + // self.mapping.lock().await.insert( + // rust_path, + // rev_mapper::get_mapping(¶ms.text_document.text), + // ); } Err(e) => { self.client @@ -353,10 +354,10 @@ impl LanguageServer for Backend { Ok(transpiled_text) => { change.text = transpiled_text; - self.mapping - .lock() - .await - .insert(rust_path, rev_mapper::get_mapping(&change.text)); + // self.mapping + // .lock() + // .await + // .insert(rust_path, rev_mapper::get_mapping(&change.text)); } Err(e) => { self.client diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index 66a316c..e3baed3 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -135,19 +135,22 @@ pub fn transpile_file(path: &Path, output_path: &Path) -> Result<(), String> { // If parsing fails, return the error string back gracefully so the LSP can show it let ast = parser_result.map_err(|e| format!("parse failed in {}:\n{}", path.display(), e))?; - let mut gc = mist_codegen::RustCodegen::new(); - let output = gc.generate(ast); + panic!(); - fs::write(output_path, output) - .map_err(|e| format!("failed to write output {}: {}", output_path.display(), e))?; + // let mut gc = mist_codegen::RustCodegen::new(); + // let output = gc.generate(ast); - Ok(()) + // fs::write(output_path, output) + // .map_err(|e| format!("failed to write output {}: {}", output_path.display(), e))?; + + // Ok(()) } pub fn transpile_text<'a>(source: &'a str) -> Result>> { - let mut gc = mist_codegen::RustCodegen::new(); + panic!(); + // let mut gc = mist_codegen::RustCodegen::new(); - Ok(gc.generate(mist_parser::parse(&source)?)) + // Ok(gc.generate(mist_parser::parse(&source)?)) } fn should_skip(source: &Path, output: &Path) -> bool { diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index f4a00bc..2f946b0 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -9,4 +9,5 @@ keywords = ["compiler", "language", "mist"] categories = ["compilers"] [dependencies] +bytecount = "0.6.9" mist-parser = { workspace = true } diff --git a/crates/codegen/src/class_decl.rs b/crates/codegen/src/class_decl.rs index 4e9ae0a..584d5e4 100644 --- a/crates/codegen/src/class_decl.rs +++ b/crates/codegen/src/class_decl.rs @@ -522,7 +522,7 @@ fn construct_pattern(pat: &Pattern, idx: usize) -> Pattern { } pub fn gen_method_point(method: &FunctionDecl, ctx: &mut Context, cg: &mut RustCodegen) { - cg.add(&format!( + cg.add_indented(&format!( "{}fn {}{}(", method.visibility.get_rust(), method.name.get_rust(), diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index 03ac6b5..6b17a85 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -3,7 +3,12 @@ pub mod expr; pub mod statement; pub mod top_level; -use mist_parser::ast::*; +use std::path::PathBuf; + +use mist_parser::{ + ast::*, + rev_mapper::{Mapping, MistMap, RustMap}, +}; pub struct Context { pub expr_ensure_semicolon: bool, @@ -22,17 +27,20 @@ pub trait GetRust { } } -#[derive(Default)] pub struct RustCodegen { output: String, indent: usize, + pub mapping: Mapping, + position: RustMap, } impl RustCodegen { - pub fn new() -> Self { + pub fn new(mist_path: PathBuf) -> Self { Self { output: String::new(), indent: 0, + mapping: Mapping::new(mist_path), + position: RustMap(1, 0), } } @@ -41,6 +49,17 @@ impl RustCodegen { } fn add(&mut self, s: &str) { + let newline_count = bytecount::count(s.as_bytes(), b'\n'); + + if newline_count == 0 { + self.position.1 += s.len(); + } else { + self.position.0 += newline_count; + + let last_newline = s.rfind('\n').unwrap(); + self.position.1 = s.len() - last_newline - 1; + } + self.output.push_str(s); } @@ -123,21 +142,18 @@ impl GenRust for Attribute { } } -impl GetRust for Spanned { - fn get_rust(&self) -> String { - format!( - "/* {}:{} */ {}", - self.line, - self.column, - self.item.get_rust() - ) +impl GenRust for T { + fn gen_rust(&self, _: &mut Context, cg: &mut RustCodegen) { + cg.add(&self.get_rust()); } } impl GenRust for Spanned { fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) { - cg.add_indentedln(&format!("/* {}:{} */", self.line, self.column)); - cg.add_indented(""); + cg.mapping + .map + .insert((cg.position, MistMap(self.line, self.column))); + self.item.gen_rust(ctx, cg); } } diff --git a/crates/codegen/src/statement.rs b/crates/codegen/src/statement.rs index 4d0229f..f1596ee 100644 --- a/crates/codegen/src/statement.rs +++ b/crates/codegen/src/statement.rs @@ -11,12 +11,14 @@ impl GenRust for Block { for stmt in &self.statements { ctx.expr_ensure_semicolon = true; + cg.add_indented(""); stmt.gen_rust(ctx, cg); cg.addln(""); } if let Some(soft_return) = &self.soft_return { ctx.expr_ensure_semicolon = false; + cg.add_indented(""); soft_return.gen_rust(ctx, cg); cg.addln(""); } diff --git a/crates/codegen/src/top_level.rs b/crates/codegen/src/top_level.rs index 1af2afa..5bda42f 100644 --- a/crates/codegen/src/top_level.rs +++ b/crates/codegen/src/top_level.rs @@ -73,7 +73,7 @@ impl GenRust for ImplDecl { impl GenRust for FunctionDecl { fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) { - cg.add(&format!( + cg.add_indented(&format!( "{}fn {}{}(", self.visibility.get_rust(), self.name.get_rust(), @@ -97,8 +97,9 @@ impl GenRust for FunctionDecl { if let Some(body) = &self.body { cg.add(" "); body.gen_rust(ctx, cg); + cg.addln(""); } else { - cg.add(";"); + cg.addln(";"); } } } @@ -195,7 +196,9 @@ impl GenRust for TopLevelKind { cg.indent += 1; for field in fields { - cg.add_indentedln(&field.get_rust()); + cg.add_indented(""); + field.gen_rust(ctx, cg); + cg.addln(""); } cg.indent -= 1; @@ -216,7 +219,9 @@ impl GenRust for TopLevelKind { cg.indent += 1; for field in fields { - cg.add_indentedln(&(field.get_rust() + ",")); + cg.add_indented(""); + field.gen_rust(ctx, cg); + cg.addln(","); } cg.indent -= 1; diff --git a/crates/mist-api/src/builder.mist b/crates/mist-api/src/builder.mist index dd94ee7..990ccd1 100644 --- a/crates/mist-api/src/builder.mist +++ b/crates/mist-api/src/builder.mist @@ -1,9 +1,7 @@ pub module builder; use std::collections::HashMap; -use std::env; use std::fs; -use std::path::MAIN_SEPARATOR; use std::path::PathBuf; use std::process::Command; use std::process::Stdio; @@ -31,8 +29,6 @@ pub fn build(mut args Vec, root PathBuf) bool { args.remove(0); args.insert(1, "--message-format=json".to_string()); - let is_root = root == env::current_dir().expect("Failed getting env"); - let mut command = Command::new("cargo") .args(args) .stdout(Stdio::piped()) @@ -47,8 +43,6 @@ pub fn build(mut args Vec, root PathBuf) bool { let mut mapping = HashMap::new(); - let mist_src = format!(".mist{MAIN_SEPARATOR}src"); - for message in cargo_metadata::Message::parse_stream(&mut reader) { match message { Ok[Message::CompilerMessage[msg]] => { @@ -56,29 +50,23 @@ pub fn build(mut args Vec, root PathBuf) bool { if span.is_primary { let rust_path = root.join(&span.file_name); - let mist_file = span - .file_name - .replacen(&mist_src, "src", 1) - .trim_end_matches(".rs") - .to_string() - + ".mist"; + let mapping_path = rust_path.with_extension("map.json"); - let mist_path = root.join(&mist_file); - - if !fs::exists(&mist_path).expect("Unable to check if mist file exists") { + if !fs::exists(&mapping_path).expect("Unable to check if map file exists") { diagnostics.push(MistDiagnostic::Rust(msg)); break; } - let map = mapping.entry(rust_path.clone()).or_insert_with(fn() { - rev_mapper::get_mapping( - &fs::read_to_string(rust_path) - .expect("Failed to read file for mapping") - ) + let map = mapping.entry(mapping_path.clone()).or_insert_with(fn() { + let map_str = fs::read_to_string(mapping_path).expect("Failed to read map file"); + + serde_json::from_str(&map_str).expect("Failed to parse map file") }); + let mist_file = map.mist_path.strip_prefix(&root); + let [_, rev_mapper::MistMap[line, column]] = - rev_mapper::find_mapping(&map, &rev_mapper::RustMap(span.line_end, span.column_start)) + map.find(&rev_mapper::RustMap(span.line_end, span.column_start)) .expect("Unable to find mapping"); let mist_msg = MistDiagnosticMessage { @@ -87,13 +75,12 @@ pub fn build(mut args Vec, root PathBuf) bool { msg.message.message, span.label.clone().unwrap_or_default() ), - file_name: if is_root { - (); // TODO: fix this - mist_file + file_name: if let Ok(mist_file) = mist_file { + mist_file.to_string_lossy().to_string() } else { - mist_path.to_string_lossy().to_string() + map.mist_path.to_string_lossy().to_string() }, - file_path: mist_path, + file_path: map.mist_path.clone(), line: line, column: column }; diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index ba66a5f..42a8302 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -200,7 +200,7 @@ fn transpile_file(path *PathBuf, source *str, mut output_file PathBuf, mod_decl } } - let mut gc = mist_codegen::RustCodegen::new(); + let mut gc = mist_codegen::RustCodegen::new(path.clone()); let parsed = match mist_parser::parse(source) { Ok[ast] => ast; @@ -238,13 +238,29 @@ fn transpile_file(path *PathBuf, source *str, mut output_file PathBuf, mod_decl let res = fs::write(&output_file, format!("{mod_decl}{output}")); + gc.mapping.shift_rust(mod_decl.lines().count() as isize, 0); + + let output_file_map = output_file.with_extension("map.json"); + + let res_map = fs::write(&output_file_map, serde_json::to_string(&gc.mapping).unwrap()); + + if res_map.is_err() { + eprintln!( + "error: failed to write mapping output {}\n {}", + output_file_map.display(), + res.as_ref().unwrap_err(), + ); + } + if res.is_err() { eprintln!( "error: failed to write output {}\n {}", output_file.display(), - res.unwrap_err(), + res.as_ref().unwrap_err(), ); + } + if res_map.is_err() || res.is_err() { process::exit(1); } } diff --git a/crates/parser/src/rev_mapper/mod.rs b/crates/parser/src/rev_mapper/mod.rs index 3e7a4d0..e943792 100644 --- a/crates/parser/src/rev_mapper/mod.rs +++ b/crates/parser/src/rev_mapper/mod.rs @@ -1,51 +1,70 @@ -use std::collections::HashSet; +use std::{collections::HashSet, path::PathBuf}; -use pest::Parser; -use pest_derive::Parser; +use serde::{Deserialize, Serialize}; -#[derive(Parser)] -#[grammar = "./src/rev_mapper/grammar.pest"] -pub struct MistMapperParser; - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, +)] pub struct MistMap(pub usize, pub usize); -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, +)] pub struct RustMap(pub usize, pub usize); -pub fn get_mapping(input: &str) -> HashSet<(RustMap, MistMap)> { - let mut mapping = HashSet::new(); +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Mapping { + pub mist_path: PathBuf, + pub map: HashSet<(RustMap, MistMap)>, +} - let pairs = MistMapperParser::parse(Rule::mapping, input) - .unwrap() - .next() - .unwrap() - .into_inner(); - - for pair in pairs { - if pair.as_rule() == Rule::map { - let rs = pair.as_span().start_pos().line_col(); - let mut inner = pair.into_inner(); - - let (line, col) = ( - inner.next().unwrap().as_str().parse().unwrap(), - inner.next().unwrap().as_str().parse().unwrap(), - ); - - mapping.insert((RustMap(rs.0, rs.1), MistMap(line, col))); +impl Mapping { + pub fn new(mist_path: PathBuf) -> Self { + Self { + mist_path, + map: HashSet::new(), } } - mapping + pub fn find(&self, target: &RustMap) -> Option<(RustMap, MistMap)> { + self.map + .iter() + .copied() + .filter(|(rust, _)| rust <= target) + .max_by_key(|(rust, _)| *rust) + } + + pub fn shift_rust(&mut self, lines: isize, cols: isize) { + self.map = self + .map + .iter() + .map(|(rust, mist)| (rust.shifted(lines, cols), *mist)) + .collect(); + } + + pub fn shift_mist(&mut self, lines: isize, cols: isize) { + self.map = self + .map + .iter() + .map(|(rust, mist)| (*rust, mist.shifted(lines, cols))) + .collect(); + } } -pub fn find_mapping( - mapping: &HashSet<(RustMap, MistMap)>, - target: &RustMap, -) -> Option<(RustMap, MistMap)> { - mapping - .iter() - .copied() - .filter(|(rust, _)| rust <= target) - .max_by_key(|(rust, _)| *rust) +impl RustMap { + pub fn shifted(self, lines: isize, cols: isize) -> Self { + Self( + self.0.saturating_add_signed(lines), + self.1.saturating_add_signed(cols), + ) + } +} + +impl MistMap { + pub fn shifted(self, lines: isize, cols: isize) -> Self { + Self( + self.0.saturating_add_signed(lines), + self.1.saturating_add_signed(cols), + ) + } }