diff --git a/.gitignore b/.gitignore index 89b5f0b..21951ac 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ **/target/ **/Cargo.lock +mers_language_server/mers_language_server tree-sitter-mers/log.html tree-sitter-mers/package-lock.json tree-sitter-mers/node_modules diff --git a/mers_language_server/Cargo.toml b/mers_language_server/Cargo.toml new file mode 100644 index 0000000..5690c5b --- /dev/null +++ b/mers_language_server/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mers_language_server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +mers_lib = { path = "../mers_lib", features = ["parse"] } +tokio = { version = "1.34.0", default-features = false, features = ["macros", "rt", "io-std"] } +tower-lsp = "0.20.0" +line-span = "0.1.5" diff --git a/mers_language_server/src/main.rs b/mers_language_server/src/main.rs new file mode 100644 index 0000000..6c277d3 --- /dev/null +++ b/mers_language_server/src/main.rs @@ -0,0 +1,484 @@ +use std::{collections::HashMap, sync::Arc}; + +use line_span::LineSpanExt; +use mers_lib::{errors::CheckError, prelude_compile::Source}; +use tokio::sync::Mutex; +use tower_lsp::{ + lsp_types::{ + CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams, + CodeActionProviderCapability, CodeActionResponse, DidChangeTextDocumentParams, + DidOpenTextDocumentParams, DocumentChanges, Hover, HoverContents, HoverParams, + HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, + MarkedString, MessageType, OneOf, OptionalVersionedTextDocumentIdentifier, Position, + ServerCapabilities, TextDocumentEdit, TextDocumentItem, TextDocumentSyncCapability, + TextDocumentSyncKind, TextEdit, WorkDoneProgressOptions, WorkspaceEdit, + }, + Client, LanguageServer, LspService, Server, +}; + +#[tokio::main(flavor = "current_thread")] +async fn main() { + let stdin = tokio::io::stdin(); + let stdout = tokio::io::stdout(); + + let (service, socket) = LspService::new(|client| Backend::new(client)); + Server::new(stdin, stdout, socket).serve(service).await; +} + +struct Backend { + client: Client, + main_document: Arc>>, + current_document: Arc>>, + documents: Arc>>, + last_compiled: Arc>>, +} +impl Backend { + pub fn new(client: Client) -> Self { + Self { + client, + main_document: Arc::new(Mutex::new(None)), + current_document: Arc::new(Mutex::new(None)), + documents: Arc::new(Mutex::new(HashMap::new())), + last_compiled: Arc::new(Mutex::new(None)), + } + } +} + +#[tower_lsp::async_trait] +impl LanguageServer for Backend { + async fn did_open(&self, p: DidOpenTextDocumentParams) { + *self.last_compiled.lock().await = None; + let mut md = self.main_document.lock().await; + if md.is_none() { + *md = Some(p.text_document.uri.clone()); + } + self.documents + .lock() + .await + .insert(p.text_document.uri.clone(), p.text_document); + } + async fn did_change(&self, p: DidChangeTextDocumentParams) { + *self.last_compiled.lock().await = None; + if let Some(document) = self.documents.lock().await.get_mut(&p.text_document.uri) { + for change in p.content_changes { + if let Some(_range) = change.range { + self.client + .log_message(MessageType::WARNING, "Received incremental document update") + .await; + } else { + document.text = change.text; + } + } + } else { + self.client + .log_message( + MessageType::WARNING, + "Received changes for a document that was never loaded", + ) + .await; + } + } + + async fn initialize( + &self, + _: InitializeParams, + ) -> tower_lsp::jsonrpc::Result { + // TODO: PositionEncodingKind, set char to 0-based *byte*-index + Ok(InitializeResult { + capabilities: ServerCapabilities { + hover_provider: Some(HoverProviderCapability::Simple(true)), + code_action_provider: Some(CodeActionProviderCapability::Options( + CodeActionOptions { + code_action_kinds: Some(vec![CodeActionKind::new("Test")]), + work_done_progress_options: WorkDoneProgressOptions::default(), + resolve_provider: None, + }, + )), + text_document_sync: Some(TextDocumentSyncCapability::Kind( + TextDocumentSyncKind::FULL, + )), + ..Default::default() + }, + ..Default::default() + }) + } + + async fn initialized(&self, _: InitializedParams) { + self.client + .log_message(MessageType::INFO, "server initialized!") + .await; + } + + async fn shutdown(&self) -> tower_lsp::jsonrpc::Result<()> { + Ok(()) + } + + async fn hover(&self, params: HoverParams) -> tower_lsp::jsonrpc::Result> { + *self.last_compiled.lock().await = None; + let byte_pos = { + match self.main_document.lock().await.as_ref() { + Some(uri) => { + let doc = self.documents.lock().await; + let doc = doc.get(uri); + let doc = doc.map(|doc| doc.text.as_str()).unwrap_or(""); + let pos_in_og = get_byte_pos_in_og( + doc, + params.text_document_position_params.position.line as _, + params.text_document_position_params.position.character as _, + ); + Some( + mers_lib::prelude_compile::Source::new_from_string(doc.to_owned()) + .pos_from_og(pos_in_og, false), + ) + } + None => None, + } + }; + let mut infos_at_cursor_hook_index = None; + let pcc = self + .parse_compile_check(|i1, i3| { + i1.global.enable_hooks = true; + i3.global.enable_hooks = true; + if let Some(byte_pos) = byte_pos { + let mut i1sia = i1.global.save_info_at.lock().unwrap(); + let mut i3sia = i3.global.save_info_at.lock().unwrap(); + infos_at_cursor_hook_index = Some((i1sia.len(), i3sia.len())); + i1sia.push((vec![], byte_pos, 2)); // 2 -> ignore outer scope + i3sia.push((vec![], byte_pos, 2)); // -> global error doesn't show up in "local" section + } + }) + .await; + Ok(Some(Hover { + contents: HoverContents::Scalar(MarkedString::String(match pcc.as_ref().unwrap() { + ParseCompileCheckResult::NoMainDocument => { + format!("# No main document\n\n(open at least one file!)") + } + ParseCompileCheckResult::MainDocumentNotFound => { + format!("# Main document not found\n\n(probably a bug)") + } + ParseCompileCheckResult::ErrorWhileParsing(_, _, e) => { + format!("# Error (can't parse):\n```\n{e}```") + } + ParseCompileCheckResult::ErrorWhileCompiling(_, _, e, _, info1) => { + let i1sia = info1.global.save_info_at.lock().unwrap(); + format!( + "# Error (can't compile):{}\n## Global error:\n\n```\n{e}```", + if let Some(i1) = infos_at_cursor_hook_index.map(|(i, _)| &i1sia[i].0) { + format!( + "\n\n## Local errors:{}\n---\n", + i1.iter() + .filter_map(|(src_range, _info, res)| { + let src_snippet = src_range.in_file().src() + [src_range.start().pos()..src_range.end().pos()] + .replace("\n", " ") + .replace("\r", " "); + match res { + Ok(()) => None, + Err(e) => { + Some(format!("\n- `{src_snippet}`\n```\n{e}```\n")) + } + } + }) + .collect::() + ) + } else { + format!("") + } + ) + } + ParseCompileCheckResult::DoesntPassChecks(_, _, e, _, _, _, info3) => { + let i3sia = info3.global.save_info_at.lock().unwrap(); + format!( + "# Error (doesn't pass checks):\n```\n{e}```{}", + if let Some(i3) = infos_at_cursor_hook_index.map(|(_, i)| &i3sia[i].0) { + format!( + "\n\n## Local types:{}", + i3.iter() + .map(|(src_range, _info, res)| { + let src_snippet = src_range.in_file().src() + [src_range.start().pos()..src_range.end().pos()] + .replace("\n", " ") + .replace("\r", " "); + match res { + Ok(local_type) => { + format!("\n- `{src_snippet}` :: `{local_type}`") + } + Err(e) => { + format!( + "\n- `{src_snippet}` :: ! Err !\n```\n{e}```\n" + ) + } + } + }) + .collect::() + ) + } else { + format!("") + } + ) + } + ParseCompileCheckResult::Checked(_, _, out_type, _, _, _, info3) => { + let i3sia = info3.global.save_info_at.lock().unwrap(); + if let Some(i3) = infos_at_cursor_hook_index.map(|(_, i)| &i3sia[i].0) { + format!( + "## Local types:{}", + i3.iter() + .map(|(src_range, _info, res)| { + let src_snippet = src_range.in_file().src() + [src_range.start().pos()..src_range.end().pos()] + .replace("\n", " ") + .replace("\r", " "); + match res { + Ok(local_type) => { + format!("\n- `{src_snippet}` :: `{local_type}`") + } + Err(e) => { + format!("\n- `{src_snippet}` :: ! Err !\n```\n{e}```\n") + } + } + }) + .collect::() + ) + } else { + format!("Program's return type: `{out_type}`") + } + } + })), + range: None, + })) + } + + async fn code_action( + &self, + params: CodeActionParams, + ) -> tower_lsp::jsonrpc::Result> { + Ok(Some( + if let Some(doc) = self.documents.lock().await.get(¶ms.text_document.uri) { + let mut src = mers_lib::prelude_compile::Source::new_from_string(doc.text.clone()); + let srca = Arc::new(src.clone()); + match mers_lib::prelude_compile::parse(&mut src, &srca) { + Err(_) => return Ok(None), + Ok(parsed) => { + let pos_start = srca.pos_from_og( + get_byte_pos_in_og( + srca.src_og(), + params.range.start.line as _, + params.range.start.character as _, + ), + false, + ); + let pos_end = srca.pos_from_og( + get_byte_pos_in_og( + srca.src_og(), + params.range.end.line as _, + params.range.end.character as _, + ), + true, + ); + let mut statements = vec![]; + iter_over_statements(parsed.as_ref(), &mut |s| { + // Wrap in `( )` + if s.source_range().start().pos() <= pos_start + && pos_end <= s.source_range().end().pos() + { + statements.push(s); + s.inner_statements() + } else { + vec![] + } + }); + let statements = statements + .into_iter() + .rev() + .take(3) + .map(|s| { + ( + s.source_range() + .in_file() + .pos_in_og(s.source_range().start().pos(), true), + s.source_range() + .in_file() + .pos_in_og(s.source_range().end().pos(), false), + s, + ) + }) + .collect::>(); + let mut actions = vec![]; + + // Wrap in `( )` + for (og_start, og_end, statement) in &statements { + actions.push(CodeActionOrCommand::CodeAction(CodeAction { + title: format!( + "Wrap `{}` in `( )`", + srca.src()[statement.source_range().start().pos() + ..statement.source_range().end().pos()] + .trim() + .replace("\n", " ") + .replace("\r", " ") + ), + edit: Some(WorkspaceEdit { + changes: None, + document_changes: Some(DocumentChanges::Edits(vec![ + TextDocumentEdit { + text_document: + OptionalVersionedTextDocumentIdentifier { + uri: params.text_document.uri.clone(), + version: None, + }, + edits: vec![OneOf::Left(TextEdit { + range: tower_lsp::lsp_types::Range { + start: get_lsp_pos_in_og( + statement.source_range().in_file().src_og(), + *og_start, + ), + end: get_lsp_pos_in_og( + statement.source_range().in_file().src_og(), + *og_end, + ), + }, + new_text: format!( + "({})", + &statement.source_range().in_file().src_og() + [*og_start..*og_end] + ), + })], + }, + ])), + ..Default::default() + }), + ..Default::default() + })); + } + + actions + } + } + } else { + return Ok(None); + }, + )) + } +} + +/// Recursively iterate over statements. +fn iter_over_statements<'b, S>(root: S, for_each: &'b mut impl FnMut(S) -> Vec) { + for i in for_each(root) { + iter_over_statements(i, for_each); + } +} + +fn get_byte_pos_in_og(og: &str, line: usize, character: usize) -> usize { + let line = og.line_spans().skip(line).next(); + if let Some(line) = line { + line.start() + character + } else { + og.len() + } +} +fn get_lsp_pos_in_og(og: &str, pos: usize) -> Position { + let mut fallback = Position { + line: 0, + character: 0, + }; + for (line_nr, line) in og.line_spans().enumerate() { + fallback = Position { + line: line_nr as _, + character: line.as_str().len() as _, + }; + if pos <= line.end() { + return Position { + line: line_nr as _, + character: (pos.saturating_sub(line.start())).min(line.as_str().len()) as _, + }; + } + } + fallback +} + +enum ParseCompileCheckResult { + NoMainDocument, + MainDocumentNotFound, + ErrorWhileParsing(Source, Arc, CheckError), + ErrorWhileCompiling( + Source, + Arc, + CheckError, + Box, + mers_lib::program::parsed::Info, + ), + DoesntPassChecks( + Source, + Arc, + CheckError, + Box, + Box, + mers_lib::program::parsed::Info, + mers_lib::program::run::CheckInfo, + ), + Checked( + Source, + Arc, + mers_lib::data::Type, + Box, + Box, + mers_lib::program::parsed::Info, + mers_lib::program::run::CheckInfo, + ), +} +impl Backend { + /// is guaranteed to return `MutexGuard`. + async fn parse_compile_check( + &self, + func_modify_infos: impl FnOnce( + &mut mers_lib::program::parsed::Info, + &mut mers_lib::program::run::CheckInfo, + ), + ) -> tokio::sync::MutexGuard<'_, Option> { + let mut last_compiled = self.last_compiled.lock().await; + if last_compiled.is_none() { + *last_compiled = Some( + if let Some(source) = self.main_document.lock().await.clone() { + if let Some(document) = self.documents.lock().await.get(&source) { + let src_from = match document.uri.to_file_path() { + Ok(path) => mers_lib::parsing::SourceFrom::File(path), + Err(_) => mers_lib::parsing::SourceFrom::Unspecified, + }; + let mut src = + mers_lib::prelude_compile::Source::new(src_from, document.text.clone()); + let srca = Arc::new(src.clone()); + match mers_lib::parsing::parse(&mut src, &srca) { + Err(e) => ParseCompileCheckResult::ErrorWhileParsing(src, srca, e), + Ok(parsed) => { + let (mut i1, _, mut i3) = mers_lib::prelude_compile::Config::new() + .bundle_std() + .infos(); + func_modify_infos(&mut i1, &mut i3); + match parsed.compile( + &mut i1, + mers_lib::prelude_compile::CompInfo::default(), + ) { + Err(e) => ParseCompileCheckResult::ErrorWhileCompiling( + src, srca, e, parsed, i1, + ), + Ok(compiled) => match compiled.check(&mut i3, None) { + Err(e) => ParseCompileCheckResult::DoesntPassChecks( + src, srca, e, parsed, compiled, i1, i3, + ), + Ok(out_type) => ParseCompileCheckResult::Checked( + src, srca, out_type, parsed, compiled, i1, i3, + ), + }, + } + } + } + } else { + ParseCompileCheckResult::MainDocumentNotFound + } + } else { + ParseCompileCheckResult::NoMainDocument + }, + ); + } + last_compiled + } +} diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index c793c2e..6ce9c84 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -17,6 +17,10 @@ pub struct Function { pub info_check: Arc>, pub out: Arc Result + Send + Sync>, pub run: Arc Data + Send + Sync>, + pub inner_statements: Option<( + Arc>, + Arc>, + )>, } impl Function { pub fn with_info_run(&self, info: Arc) -> Self { @@ -25,6 +29,10 @@ impl Function { info_check: Arc::clone(&self.info_check), out: Arc::clone(&self.out), run: Arc::clone(&self.run), + inner_statements: self + .inner_statements + .as_ref() + .map(|v| (Arc::clone(&v.0), Arc::clone(&v.1))), } } pub fn with_info_check(&self, check: CheckInfo) { diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index 050e5e1..d524ec5 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -39,8 +39,12 @@ impl SourceRange { pub fn end(&self) -> SourcePos { self.end } + pub fn in_file(&self) -> &Arc { + &self.in_file + } } -pub struct CheckError(Vec); +#[derive(Clone)] +pub struct CheckError(pub Vec); #[allow(non_upper_case_globals)] pub mod error_colors { use colored::Color; @@ -76,7 +80,8 @@ pub mod error_colors { pub const BadTypeFromParsed: Color = Color::Blue; pub const TypeAnnotationNoClosingBracket: Color = Color::Blue; } -enum CheckErrorComponent { +#[derive(Clone)] +pub enum CheckErrorComponent { Message(String), Error(CheckError), ErrorWithDifferentSource(CheckError), diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index 9e68d27..e69770d 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -1,5 +1,7 @@ use std::{fmt::Debug, path::PathBuf, sync::Arc}; +use line_span::{LineSpan, LineSpanExt}; + use crate::{ errors::{CheckError, SourcePos}, program::{self, parsed::block::Block}, @@ -306,9 +308,37 @@ impl Source { } pos } + pub fn pos_from_og(&self, mut pos: usize, behind_comment: bool) -> usize { + for (start, comment) in &self.comments { + if *start + comment.len() <= pos { + pos -= comment.len(); + } else if *start <= pos { + return if behind_comment { + *start + comment.len() + } else { + *start + }; + } else { + break; + } + } + pos + } pub fn src_og(&self) -> &String { &self.src_og } + /// If found, returns `Some((line_nr, line_str, byte_pos_in_line_may_be_out_of_string_bounds))` + pub fn get_line_and_char_from_pos_in_str( + byte_index: usize, + str: &str, + ) -> Option<(usize, LineSpan<'_>, usize)> { + for (line, line_span) in str.line_spans().enumerate() { + if line_span.start() <= byte_index { + return Some((line, line_span, byte_index - line_span.start())); + } + } + None + } } impl Drop for Source { diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 8fd0f5a..8ad7cfe 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -123,7 +123,7 @@ pub fn parse( .msg(format!("EOF after `:=`")) })?; first = Box::new(program::parsed::init_to::InitTo { - pos_in_src: (pos_in_src, src.get_pos(), srca).into(), + pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(), target: first, source, }); @@ -133,7 +133,10 @@ pub fn parse( src.next_word_allow_colon(); let source = parse(src, srca)?.ok_or_else(|| { CheckError::new() - .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) + .src(vec![( + (first.source_range().start(), src.get_pos(), srca).into(), + None, + )]) .msg(format!("EOF after `=`")) })?; first = Box::new(program::parsed::assign_to::AssignTo { @@ -155,7 +158,7 @@ pub fn parse( Err(e) => return Err(e), }; first = Box::new(program::parsed::function::Function { - pos_in_src: (pos_in_src, src.get_pos(), srca).into(), + pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(), arg: first, run, }); diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 2553b47..e2e82c9 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -112,7 +112,8 @@ impl Config { } } unreachable!("try: no function found") - }) + }), + inner_statements: None, })) .add_var("lock_update".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), @@ -160,7 +161,8 @@ impl Config { let func = func.as_any().downcast_ref::().unwrap(); *arg = func.run(arg.clone()); Data::empty_tuple() - }) + }), + inner_statements: None, })) .add_var("sleep".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), @@ -183,7 +185,8 @@ impl Config { unreachable!("sleep called on non-int/non-float") }); Data::empty_tuple() - }) + }), + inner_statements: None, })) .add_var("panic".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), @@ -195,7 +198,8 @@ impl Config { }), run: Arc::new(|a, _i| { std::process::exit(a.get().as_any().downcast_ref::().map(|i| i.0 as _).unwrap_or(1)); - }) + }), + inner_statements: None, })) .add_var( "len".to_string(), @@ -222,6 +226,7 @@ impl Config { unreachable!("called len on {a:?}, which isn't a tuple or a string") })) }), + inner_statements: None, }), ).add_var( "loop".to_string(), @@ -270,6 +275,7 @@ impl Config { } } }), + inner_statements: None, }), ) .add_var( @@ -303,6 +309,7 @@ impl Config { false })) }), + inner_statements: None, }), ) .add_var( @@ -323,6 +330,7 @@ impl Config { unreachable!("called deref on non-reference") } }), + inner_statements: None, }), ) } diff --git a/mers_lib/src/program/configs/with_command_running.rs b/mers_lib/src/program/configs/with_command_running.rs index 4b1083a..b7de3a9 100755 --- a/mers_lib/src/program/configs/with_command_running.rs +++ b/mers_lib/src/program/configs/with_command_running.rs @@ -76,6 +76,7 @@ impl Config { unreachable!("run_command called with non-tuple argument") } }), + inner_statements: None, }), ) } diff --git a/mers_lib/src/program/configs/with_get.rs b/mers_lib/src/program/configs/with_get.rs index 3b0c643..125a773 100755 --- a/mers_lib/src/program/configs/with_get.rs +++ b/mers_lib/src/program/configs/with_get.rs @@ -66,6 +66,7 @@ impl Config { unreachable!("get called with less than 2 args") } }), + inner_statements: None, }), ) } diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index 3210ee0..80b705b 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -98,6 +98,7 @@ impl Config { unreachable!("for_each called on non-tuple") } }), + inner_statements: None, }), ) .add_var( @@ -133,6 +134,7 @@ impl Config { Ok(Type::new(IterT::new(ItersT::Enumerate, data)?)) }), run: Arc::new(|a, _i| Data::new(Iter(Iters::Enumerate, a.clone()))), + inner_statements: None, }), ) } @@ -203,6 +205,7 @@ fn genfunc_iter_and_arg( unreachable!("{name} called on non-tuple") } }), + inner_statements: None, } } @@ -408,5 +411,6 @@ fn genfunc_iter_in_val_out( } }), run: Arc::new(run), + inner_statements: None, } } diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 2be9828..d0c3793 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -94,6 +94,7 @@ impl Config { }; o }), + inner_statements: None, })) .add_var( "pop".to_string(), @@ -140,6 +141,7 @@ impl Config { None => Data::empty_tuple(), } }), + inner_statements: None, }), ) .add_var( @@ -202,6 +204,7 @@ impl Config { .push(Arc::new(RwLock::new(tuple.0[1].clone()))); Data::empty_tuple() }), + inner_statements: None, }), ) .add_var( @@ -225,6 +228,7 @@ impl Config { unreachable!("as_list called on non-iterable") } }), + inner_statements: None, }), ) } diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs index e017b5c..ceb40bd 100755 --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -75,7 +75,8 @@ impl Config { } else { Data::empty_tuple() } - }) + }), + inner_statements: None, })).add_var("parse_int".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -95,7 +96,8 @@ impl Config { } else { Data::empty_tuple() } - }) + }), + inner_statements: None, })).add_var("signum".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -118,7 +120,8 @@ impl Config { } else { 0 } } else { unreachable!("called signum on non-number type")})) - }) + }), + inner_statements: None, })) .add_var("div".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -137,6 +140,7 @@ impl Config { _ => unreachable!(), } } else { unreachable!() }), + inner_statements: None, })).add_var("modulo".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -155,6 +159,7 @@ impl Config { _ => unreachable!(), } } else { unreachable!() }), + inner_statements: None, })) .add_var( "sum".to_string(), @@ -215,6 +220,7 @@ impl Config { unreachable!("sum called on non-tuple") } }), + inner_statements: None, }), ) .add_var( @@ -288,6 +294,7 @@ impl Config { unreachable!("sum called on non-tuple") } }), + inner_statements: None, }), ) .add_var( @@ -349,6 +356,7 @@ impl Config { unreachable!("product called on non-tuple") } }), + inner_statements: None, }), ) } @@ -428,6 +436,7 @@ fn ltgtoe_function( } Data::new(data::bool::Bool(true)) }), + inner_statements: None, } } #[derive(Clone, Copy)] diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index dc7bd81..0087f57 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -58,6 +58,7 @@ impl Config { unreachable!("thread called, but arg wasn't a function"); } }), + inner_statements: None, }), ) .add_var("thread_finished".to_string(), Data::new(data::function::Function { @@ -78,7 +79,8 @@ impl Config { Ok(t) => t.is_finished(), Err(_d) => true, })) - }) + }), + inner_statements: None, })) .add_var("thread_await".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), @@ -103,7 +105,8 @@ impl Config { }; *t = Err(d.clone()); d - }) + }), + inner_statements: None, })) } } diff --git a/mers_lib/src/program/configs/with_stdio.rs b/mers_lib/src/program/configs/with_stdio.rs index bde449c..0b2d9d4 100755 --- a/mers_lib/src/program/configs/with_stdio.rs +++ b/mers_lib/src/program/configs/with_stdio.rs @@ -42,6 +42,7 @@ impl Config { _ = std::io::stdin().read_line(&mut line); Data::new(data::string::String(line)) }), + inner_statements: None, }), ) .add_var( @@ -55,6 +56,7 @@ impl Config { eprintln!("{} :: {}", a.as_type(), a); Data::empty_tuple() }), + inner_statements: None, }), ) .add_var( @@ -68,6 +70,7 @@ impl Config { _ = std::io::stderr().lock().flush(); Data::empty_tuple() }), + inner_statements: None, }), ) .add_var( @@ -80,6 +83,7 @@ impl Config { eprintln!("{}", a.get()); Data::empty_tuple() }), + inner_statements: None, }), ) .add_var( @@ -93,6 +97,7 @@ impl Config { _ = std::io::stdout().lock().flush(); Data::empty_tuple() }), + inner_statements: None, }), ) .add_var( @@ -105,6 +110,7 @@ impl Config { println!("{}", a.get()); Data::empty_tuple() }), + inner_statements: None, }), ) } diff --git a/mers_lib/src/program/configs/with_string.rs b/mers_lib/src/program/configs/with_string.rs index 90fe9fe..0ba4049 100755 --- a/mers_lib/src/program/configs/with_string.rs +++ b/mers_lib/src/program/configs/with_string.rs @@ -25,7 +25,8 @@ impl Config { }), run: Arc::new(|a, _i| { Data::new(data::string::String(a.get().as_any().downcast_ref::().unwrap().0.trim().to_owned())) - }) + }), + inner_statements: None, })).add_var("concat".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -35,11 +36,13 @@ impl Config { Err(format!("concat called on non-iterable type {a}").into()) }), run: Arc::new(|a, _i| Data::new(data::string::String(a.get().iterable().unwrap().map(|v| v.get().to_string()).collect()))), + inner_statements: None, })).add_var("to_string".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), out: Arc::new(|_a, _i| Ok(Type::new(data::string::StringT))), run: Arc::new(|a, _i| Data::new(data::string::String(a.get().to_string()))), + inner_statements: None, })).add_var("index_of".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -52,6 +55,7 @@ impl Config { Err(format!("wrong args for index_of: must be (string, string)").into()) }), run: Arc::new(|a, _i| index_of(a, false)), + inner_statements: None, })).add_var("index_of_rev".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -64,6 +68,7 @@ impl Config { Err(format!("wrong args for index_of: must be (string, string)").into()) }), run: Arc::new(|a, _i| index_of(a, true)), + inner_statements: None, })).add_var("substring".to_string(), Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -113,6 +118,7 @@ impl Config { Data::new(data::string::String(s[start..end].to_owned())) }), + inner_statements: None, })) } } diff --git a/mers_lib/src/program/parsed/as_type.rs b/mers_lib/src/program/parsed/as_type.rs index c188246..379680a 100755 --- a/mers_lib/src/program/parsed/as_type.rs +++ b/mers_lib/src/program/parsed/as_type.rs @@ -35,4 +35,10 @@ impl MersStatement for AsType { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.statement.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/assign_to.rs b/mers_lib/src/program/parsed/assign_to.rs index aa81998..41ad834 100755 --- a/mers_lib/src/program/parsed/assign_to.rs +++ b/mers_lib/src/program/parsed/assign_to.rs @@ -31,4 +31,10 @@ impl MersStatement for AssignTo { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.source.as_ref(), self.target.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/block.rs b/mers_lib/src/program/parsed/block.rs index 2797687..3c83cd6 100755 --- a/mers_lib/src/program/parsed/block.rs +++ b/mers_lib/src/program/parsed/block.rs @@ -32,4 +32,10 @@ impl MersStatement for Block { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.statements.iter().map(|v| v.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/chain.rs b/mers_lib/src/program/parsed/chain.rs index b08c9e1..6898586 100755 --- a/mers_lib/src/program/parsed/chain.rs +++ b/mers_lib/src/program/parsed/chain.rs @@ -30,4 +30,10 @@ impl MersStatement for Chain { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.first.as_ref(), self.chained.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/custom_type.rs b/mers_lib/src/program/parsed/custom_type.rs index c54cef8..ff2d8a8 100644 --- a/mers_lib/src/program/parsed/custom_type.rs +++ b/mers_lib/src/program/parsed/custom_type.rs @@ -37,6 +37,16 @@ impl MersStatement for CustomType { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + if let Err(s) = &self.source { + vec![s.as_ref()] + } else { + vec![] + } + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl Debug for CustomType { diff --git a/mers_lib/src/program/parsed/function.rs b/mers_lib/src/program/parsed/function.rs index 6d5ac9c..759960f 100755 --- a/mers_lib/src/program/parsed/function.rs +++ b/mers_lib/src/program/parsed/function.rs @@ -29,8 +29,10 @@ impl MersStatement for Function { let arg_target = Arc::new(self.arg.compile(info, comp)?); comp.is_init = false; let run = Arc::new(self.run.compile(info, comp)?); - let arg2: Arc> = Arc::clone(&arg_target); - let run2: Arc> = Arc::clone(&run); + let arg2 = Arc::clone(&arg_target); + let run2 = Arc::clone(&run); + let arg3 = Arc::clone(&arg_target); + let run3 = Arc::clone(&run); Ok(Box::new(program::run::function::Function { pos_in_src: self.pos_in_src.clone(), func_no_info: data::function::Function { @@ -44,10 +46,17 @@ impl MersStatement for Function { data::defs::assign(&arg, &arg_target.run(info)); run.run(info) }), + inner_statements: Some((arg3, run3)), }, })) } fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.arg.as_ref(), self.run.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/if.rs b/mers_lib/src/program/parsed/if.rs index 1e37b98..d2757a2 100755 --- a/mers_lib/src/program/parsed/if.rs +++ b/mers_lib/src/program/parsed/if.rs @@ -36,4 +36,18 @@ impl MersStatement for If { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + if let Some(on_false) = &self.on_false { + vec![ + self.condition.as_ref(), + self.on_true.as_ref(), + on_false.as_ref(), + ] + } else { + vec![self.condition.as_ref(), self.on_true.as_ref()] + } + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/include_mers.rs b/mers_lib/src/program/parsed/include_mers.rs index 93f96fe..129fe60 100644 --- a/mers_lib/src/program/parsed/include_mers.rs +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -58,6 +58,7 @@ impl MersStatement for IncludeMers { info_check: Arc::new(Mutex::new(info::Info::neverused())), out: Arc::new(move |_, i| compiled.check(&mut i.duplicate(), None)), run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())), + inner_statements: None, }, }), as_part_of_include: Some(self.inner_src.clone()), @@ -66,4 +67,10 @@ impl MersStatement for IncludeMers { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index e4b2eef..0c8cfbc 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -37,4 +37,10 @@ impl MersStatement for InitTo { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.source.as_ref(), self.target.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index d8a839b..56630f1 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, fmt::Debug}; +use std::{ + collections::HashMap, + fmt::Debug, + sync::{Arc, Mutex}, +}; use crate::{ errors::{CheckError, SourceRange}, @@ -44,21 +48,53 @@ pub trait MersStatement: Debug + Send + Sync { info: &mut Info, comp: CompInfo, ) -> Result, CheckError> { + info.global.depth += 1; if self.has_scope() { info.create_scope(); } let o = self.compile_custom(info, comp); + if info.global.enable_hooks { + // Hooks - keep in sync with run/mod.rs/compile() hooks section + { + // `save_info_at` hook + let mut save_info_at = info.global.save_info_at.try_lock().unwrap(); + if !save_info_at.is_empty() { + let pos_start = self.source_range().start().pos(); + let pos_end = self.source_range().end().pos(); + let cloned_info = Arc::new(info.clone()); + for (save_to, save_at, deepest_statement) in save_info_at.iter_mut() { + if info.global.depth >= *deepest_statement + && pos_start <= *save_at + && *save_at < pos_end + { + if info.global.depth > *deepest_statement { + *deepest_statement = info.global.depth; + save_to.clear(); + } + save_to.push(( + self.source_range(), + Arc::clone(&cloned_info), + o.as_ref().map(|_| ()).map_err(|e| e.clone()), + )); + } + } + } + } + } if self.has_scope() { info.end_scope(); } + info.global.depth -= 1; o } fn source_range(&self) -> SourceRange; + fn inner_statements(&self) -> Vec<&dyn MersStatement>; + fn as_any(&self) -> &dyn std::any::Any; } #[derive(Clone, Copy)] pub struct CompInfo { - is_init: bool, + pub is_init: bool, } impl Default for CompInfo { fn default() -> Self { @@ -70,13 +106,30 @@ pub type Info = info::Info; #[derive(Default, Clone, Debug)] pub struct Local { - vars: HashMap, - vars_count: usize, + pub vars: HashMap, + pub vars_count: usize, +} +#[derive(Clone, Debug, Default)] +pub struct LocalGlobalInfo { + pub depth: usize, + pub enable_hooks: bool, + /// ((results, byte_pos_in_src, deepest_statement)) + /// you only have to set `byte_pos_in_src`. `deepest` is used internally. + /// These values should be initialized to `(vec![], _, 0)`, but `0` can be replaced by a minimum statement depth, i.e. `2` to exclude the outer scope (which has depth `1`). + pub save_info_at: Arc< + Mutex< + Vec<( + Vec<(SourceRange, Arc, Result<(), CheckError>)>, + usize, + usize, + )>, + >, + >, } impl info::Local for Local { type VariableIdentifier = String; type VariableData = (usize, usize); - type Global = (); + type Global = LocalGlobalInfo; fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { self.vars_count += 1; self.vars.insert(id, value); diff --git a/mers_lib/src/program/parsed/object.rs b/mers_lib/src/program/parsed/object.rs index 8e4d005..5d717b1 100644 --- a/mers_lib/src/program/parsed/object.rs +++ b/mers_lib/src/program/parsed/object.rs @@ -32,4 +32,10 @@ impl MersStatement for Object { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.elems.iter().map(|(_, s)| s.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/tuple.rs b/mers_lib/src/program/parsed/tuple.rs index 9215e5f..d1e03c8 100755 --- a/mers_lib/src/program/parsed/tuple.rs +++ b/mers_lib/src/program/parsed/tuple.rs @@ -32,4 +32,10 @@ impl MersStatement for Tuple { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.elems.iter().map(|v| v.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/value.rs b/mers_lib/src/program/parsed/value.rs index 7a4788e..a4528f4 100755 --- a/mers_lib/src/program/parsed/value.rs +++ b/mers_lib/src/program/parsed/value.rs @@ -29,4 +29,10 @@ impl MersStatement for Value { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index ef39bec..f4d388f 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -59,4 +59,10 @@ impl MersStatement for Variable { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/as_type.rs b/mers_lib/src/program/run/as_type.rs index dd2394a..f732224 100644 --- a/mers_lib/src/program/run/as_type.rs +++ b/mers_lib/src/program/run/as_type.rs @@ -75,4 +75,10 @@ impl MersStatement for AsType { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.statement.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 7557b16..a3fa8d1 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -83,4 +83,10 @@ impl MersStatement for AssignTo { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.target.as_ref(), self.source.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/block.rs b/mers_lib/src/program/run/block.rs index cd55409..c73a43d 100755 --- a/mers_lib/src/program/run/block.rs +++ b/mers_lib/src/program/run/block.rs @@ -38,4 +38,10 @@ impl MersStatement for Block { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.statements.iter().map(|s| s.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 6d307b5..d316473 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -101,4 +101,10 @@ impl MersStatement for Chain { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![self.first.as_ref(), self.chained.as_ref()] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/custom_type.rs b/mers_lib/src/program/run/custom_type.rs index e85f8c1..ca122f9 100644 --- a/mers_lib/src/program/run/custom_type.rs +++ b/mers_lib/src/program/run/custom_type.rs @@ -54,6 +54,12 @@ impl MersStatement for CustomType { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl Debug for CustomType { diff --git a/mers_lib/src/program/run/function.rs b/mers_lib/src/program/run/function.rs index 8af9b7f..090a9f7 100755 --- a/mers_lib/src/program/run/function.rs +++ b/mers_lib/src/program/run/function.rs @@ -34,4 +34,14 @@ impl MersStatement for Function { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + if let Some((a, b)) = &self.func_no_info.inner_statements { + vec![a.as_ref().as_ref(), b.as_ref().as_ref()] + } else { + vec![] + } + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index e2d0994..ee30a31 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -73,4 +73,18 @@ impl MersStatement for If { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + if let Some(on_false) = &self.on_false { + vec![ + self.condition.as_ref(), + self.on_true.as_ref(), + on_false.as_ref(), + ] + } else { + vec![self.condition.as_ref(), self.on_true.as_ref()] + } + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 69b73b8..bf08c11 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, fmt::Debug, - sync::{Arc, RwLock}, + sync::{Arc, Mutex, RwLock}, }; use crate::{ @@ -43,13 +43,50 @@ pub trait MersStatement: Debug + Send + Sync { /// if true, local variables etc. will be contained inside their own scope. fn has_scope(&self) -> bool; fn check(&self, info: &mut CheckInfo, assign: Option<&Type>) -> Result { + info.global.depth += 1; if self.has_scope() { info.create_scope(); } let o = self.check_custom(info, assign); + if info.global.enable_hooks { + // Hooks - keep in sync with run/mod.rs/compile() hooks section + 'hook_save_info_at: { + // `save_info_at` hook + let mut save_info_at = if let Ok(lock) = info.global.save_info_at.try_lock() { + lock + } else { + eprintln!( + "[HOOKS/save_info_at] couldn't acquire lock - result may be incomplete" + ); + break 'hook_save_info_at; + }; + if !save_info_at.is_empty() { + let pos_start = self.source_range().start().pos(); + let pos_end = self.source_range().end().pos(); + let cloned_info = Arc::new(info.clone()); + for (save_to, save_at, deepest_statement) in save_info_at.iter_mut() { + if info.global.depth >= *deepest_statement + && pos_start <= *save_at + && *save_at < pos_end + { + if info.global.depth > *deepest_statement { + *deepest_statement = info.global.depth; + save_to.clear(); + } + save_to.push(( + self.source_range(), + Arc::clone(&cloned_info), + o.clone(), + )); + } + } + } + } + } if self.has_scope() { info.end_scope(); } + info.global.depth -= 1; o } fn run(&self, info: &mut Info) -> Data { @@ -63,6 +100,8 @@ pub trait MersStatement: Debug + Send + Sync { o } fn source_range(&self) -> SourceRange; + fn inner_statements(&self) -> Vec<&dyn MersStatement>; + fn as_any(&self) -> &dyn std::any::Any; } pub type Info = info::Info; @@ -70,11 +109,11 @@ pub type CheckInfo = info::Info; #[derive(Default, Clone, Debug)] pub struct Local { - vars: Vec>>, + pub vars: Vec>>, } #[derive(Default, Clone)] pub struct CheckLocal { - vars: Vec, + pub vars: Vec, pub types: HashMap< String, Result< @@ -83,6 +122,23 @@ pub struct CheckLocal { >, >, } +#[derive(Clone, Debug, Default)] +pub struct CheckLocalGlobalInfo { + pub depth: usize, + pub enable_hooks: bool, + /// ((results, byte_pos_in_src, deepest_statement)) + /// you only have to set `byte_pos_in_src`. `deepest` is used internally. + /// These values should be initialized to `(vec![], _, 0)`, but `0` can be replaced by a minimum statement depth, i.e. `2` to exclude the outer scope (which has depth `1`). + pub save_info_at: Arc< + Mutex< + Vec<( + Vec<(SourceRange, Arc, Result)>, + usize, + usize, + )>, + >, + >, +} impl Debug for CheckLocal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys()) @@ -124,7 +180,7 @@ impl info::Local for Local { impl info::Local for CheckLocal { type VariableIdentifier = usize; type VariableData = Type; - type Global = (); + type Global = CheckLocalGlobalInfo; fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { while self.vars.len() <= id { self.vars.push(Type::empty()); diff --git a/mers_lib/src/program/run/object.rs b/mers_lib/src/program/run/object.rs index a54a712..dad4eab 100644 --- a/mers_lib/src/program/run/object.rs +++ b/mers_lib/src/program/run/object.rs @@ -133,4 +133,10 @@ impl MersStatement for Object { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.elems.iter().map(|(_, s)| s.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index 93158cc..048b547 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -93,4 +93,10 @@ impl MersStatement for Tuple { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + self.elems.iter().map(|s| s.as_ref()).collect() + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/value.rs b/mers_lib/src/program/run/value.rs index 8ca74a4..166e809 100755 --- a/mers_lib/src/program/run/value.rs +++ b/mers_lib/src/program/run/value.rs @@ -31,4 +31,10 @@ impl MersStatement for Value { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/mers_lib/src/program/run/variable.rs b/mers_lib/src/program/run/variable.rs index 5755315..38fba55 100755 --- a/mers_lib/src/program/run/variable.rs +++ b/mers_lib/src/program/run/variable.rs @@ -79,4 +79,10 @@ impl MersStatement for Variable { fn source_range(&self) -> SourceRange { self.pos_in_src.clone() } + fn inner_statements(&self) -> Vec<&dyn MersStatement> { + vec![] + } + fn as_any(&self) -> &dyn std::any::Any { + self + } }