mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
early prototype of a language server
currently only provides hover info (but that's already quite useful for debugging)
This commit is contained in:
parent
8b60da8d99
commit
0759b769e0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
**/target/
|
**/target/
|
||||||
**/Cargo.lock
|
**/Cargo.lock
|
||||||
|
mers_language_server/mers_language_server
|
||||||
tree-sitter-mers/log.html
|
tree-sitter-mers/log.html
|
||||||
tree-sitter-mers/package-lock.json
|
tree-sitter-mers/package-lock.json
|
||||||
tree-sitter-mers/node_modules
|
tree-sitter-mers/node_modules
|
||||||
|
12
mers_language_server/Cargo.toml
Normal file
12
mers_language_server/Cargo.toml
Normal file
@ -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"
|
484
mers_language_server/src/main.rs
Normal file
484
mers_language_server/src/main.rs
Normal file
@ -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<Mutex<Option<tower_lsp::lsp_types::Url>>>,
|
||||||
|
current_document: Arc<Mutex<Option<tower_lsp::lsp_types::Url>>>,
|
||||||
|
documents: Arc<Mutex<HashMap<tower_lsp::lsp_types::Url, TextDocumentItem>>>,
|
||||||
|
last_compiled: Arc<Mutex<Option<ParseCompileCheckResult>>>,
|
||||||
|
}
|
||||||
|
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<InitializeResult> {
|
||||||
|
// 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<Option<Hover>> {
|
||||||
|
*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::<String>()
|
||||||
|
)
|
||||||
|
} 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::<String>()
|
||||||
|
)
|
||||||
|
} 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::<String>()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("Program's return type: `{out_type}`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
range: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn code_action(
|
||||||
|
&self,
|
||||||
|
params: CodeActionParams,
|
||||||
|
) -> tower_lsp::jsonrpc::Result<Option<CodeActionResponse>> {
|
||||||
|
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::<Vec<_>>();
|
||||||
|
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<S>) {
|
||||||
|
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<Source>, CheckError),
|
||||||
|
ErrorWhileCompiling(
|
||||||
|
Source,
|
||||||
|
Arc<Source>,
|
||||||
|
CheckError,
|
||||||
|
Box<dyn mers_lib::prelude_compile::ParsedMersStatement>,
|
||||||
|
mers_lib::program::parsed::Info,
|
||||||
|
),
|
||||||
|
DoesntPassChecks(
|
||||||
|
Source,
|
||||||
|
Arc<Source>,
|
||||||
|
CheckError,
|
||||||
|
Box<dyn mers_lib::prelude_compile::ParsedMersStatement>,
|
||||||
|
Box<dyn mers_lib::prelude_compile::RunMersStatement>,
|
||||||
|
mers_lib::program::parsed::Info,
|
||||||
|
mers_lib::program::run::CheckInfo,
|
||||||
|
),
|
||||||
|
Checked(
|
||||||
|
Source,
|
||||||
|
Arc<Source>,
|
||||||
|
mers_lib::data::Type,
|
||||||
|
Box<dyn mers_lib::prelude_compile::ParsedMersStatement>,
|
||||||
|
Box<dyn mers_lib::prelude_compile::RunMersStatement>,
|
||||||
|
mers_lib::program::parsed::Info,
|
||||||
|
mers_lib::program::run::CheckInfo,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
impl Backend {
|
||||||
|
/// is guaranteed to return `MutexGuard<Some(_)>`.
|
||||||
|
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<ParseCompileCheckResult>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,10 @@ pub struct Function {
|
|||||||
pub info_check: Arc<Mutex<CheckInfo>>,
|
pub info_check: Arc<Mutex<CheckInfo>>,
|
||||||
pub out: Arc<dyn Fn(&Type, &mut CheckInfo) -> Result<Type, CheckError> + Send + Sync>,
|
pub out: Arc<dyn Fn(&Type, &mut CheckInfo) -> Result<Type, CheckError> + Send + Sync>,
|
||||||
pub run: Arc<dyn Fn(Data, &mut crate::program::run::Info) -> Data + Send + Sync>,
|
pub run: Arc<dyn Fn(Data, &mut crate::program::run::Info) -> Data + Send + Sync>,
|
||||||
|
pub inner_statements: Option<(
|
||||||
|
Arc<Box<dyn crate::prelude_compile::RunMersStatement>>,
|
||||||
|
Arc<Box<dyn crate::prelude_compile::RunMersStatement>>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn with_info_run(&self, info: Arc<Info>) -> Self {
|
pub fn with_info_run(&self, info: Arc<Info>) -> Self {
|
||||||
@ -25,6 +29,10 @@ impl Function {
|
|||||||
info_check: Arc::clone(&self.info_check),
|
info_check: Arc::clone(&self.info_check),
|
||||||
out: Arc::clone(&self.out),
|
out: Arc::clone(&self.out),
|
||||||
run: Arc::clone(&self.run),
|
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) {
|
pub fn with_info_check(&self, check: CheckInfo) {
|
||||||
|
@ -39,8 +39,12 @@ impl SourceRange {
|
|||||||
pub fn end(&self) -> SourcePos {
|
pub fn end(&self) -> SourcePos {
|
||||||
self.end
|
self.end
|
||||||
}
|
}
|
||||||
|
pub fn in_file(&self) -> &Arc<Source> {
|
||||||
|
&self.in_file
|
||||||
}
|
}
|
||||||
pub struct CheckError(Vec<CheckErrorComponent>);
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CheckError(pub Vec<CheckErrorComponent>);
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub mod error_colors {
|
pub mod error_colors {
|
||||||
use colored::Color;
|
use colored::Color;
|
||||||
@ -76,7 +80,8 @@ pub mod error_colors {
|
|||||||
pub const BadTypeFromParsed: Color = Color::Blue;
|
pub const BadTypeFromParsed: Color = Color::Blue;
|
||||||
pub const TypeAnnotationNoClosingBracket: Color = Color::Blue;
|
pub const TypeAnnotationNoClosingBracket: Color = Color::Blue;
|
||||||
}
|
}
|
||||||
enum CheckErrorComponent {
|
#[derive(Clone)]
|
||||||
|
pub enum CheckErrorComponent {
|
||||||
Message(String),
|
Message(String),
|
||||||
Error(CheckError),
|
Error(CheckError),
|
||||||
ErrorWithDifferentSource(CheckError),
|
ErrorWithDifferentSource(CheckError),
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::{fmt::Debug, path::PathBuf, sync::Arc};
|
use std::{fmt::Debug, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
|
use line_span::{LineSpan, LineSpanExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{CheckError, SourcePos},
|
errors::{CheckError, SourcePos},
|
||||||
program::{self, parsed::block::Block},
|
program::{self, parsed::block::Block},
|
||||||
@ -306,9 +308,37 @@ impl Source {
|
|||||||
}
|
}
|
||||||
pos
|
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 {
|
pub fn src_og(&self) -> &String {
|
||||||
&self.src_og
|
&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 {
|
impl Drop for Source {
|
||||||
|
@ -123,7 +123,7 @@ pub fn parse(
|
|||||||
.msg(format!("EOF after `:=`"))
|
.msg(format!("EOF after `:=`"))
|
||||||
})?;
|
})?;
|
||||||
first = Box::new(program::parsed::init_to::InitTo {
|
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,
|
target: first,
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
@ -133,7 +133,10 @@ pub fn parse(
|
|||||||
src.next_word_allow_colon();
|
src.next_word_allow_colon();
|
||||||
let source = parse(src, srca)?.ok_or_else(|| {
|
let source = parse(src, srca)?.ok_or_else(|| {
|
||||||
CheckError::new()
|
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 `=`"))
|
.msg(format!("EOF after `=`"))
|
||||||
})?;
|
})?;
|
||||||
first = Box::new(program::parsed::assign_to::AssignTo {
|
first = Box::new(program::parsed::assign_to::AssignTo {
|
||||||
@ -155,7 +158,7 @@ pub fn parse(
|
|||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
first = Box::new(program::parsed::function::Function {
|
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,
|
arg: first,
|
||||||
run,
|
run,
|
||||||
});
|
});
|
||||||
|
@ -112,7 +112,8 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!("try: no function found")
|
unreachable!("try: no function found")
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var("lock_update".to_string(), Data::new(data::function::Function {
|
.add_var("lock_update".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
@ -160,7 +161,8 @@ impl Config {
|
|||||||
let func = func.as_any().downcast_ref::<data::function::Function>().unwrap();
|
let func = func.as_any().downcast_ref::<data::function::Function>().unwrap();
|
||||||
*arg = func.run(arg.clone());
|
*arg = func.run(arg.clone());
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var("sleep".to_string(), Data::new(data::function::Function {
|
.add_var("sleep".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
@ -183,7 +185,8 @@ impl Config {
|
|||||||
unreachable!("sleep called on non-int/non-float")
|
unreachable!("sleep called on non-int/non-float")
|
||||||
});
|
});
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var("panic".to_string(), Data::new(data::function::Function {
|
.add_var("panic".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
@ -195,7 +198,8 @@ impl Config {
|
|||||||
}),
|
}),
|
||||||
run: Arc::new(|a, _i| {
|
run: Arc::new(|a, _i| {
|
||||||
std::process::exit(a.get().as_any().downcast_ref::<data::int::Int>().map(|i| i.0 as _).unwrap_or(1));
|
std::process::exit(a.get().as_any().downcast_ref::<data::int::Int>().map(|i| i.0 as _).unwrap_or(1));
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var(
|
.add_var(
|
||||||
"len".to_string(),
|
"len".to_string(),
|
||||||
@ -222,6 +226,7 @@ impl Config {
|
|||||||
unreachable!("called len on {a:?}, which isn't a tuple or a string")
|
unreachable!("called len on {a:?}, which isn't a tuple or a string")
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
).add_var(
|
).add_var(
|
||||||
"loop".to_string(),
|
"loop".to_string(),
|
||||||
@ -270,6 +275,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -303,6 +309,7 @@ impl Config {
|
|||||||
false
|
false
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -323,6 +330,7 @@ impl Config {
|
|||||||
unreachable!("called deref on non-reference")
|
unreachable!("called deref on non-reference")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ impl Config {
|
|||||||
unreachable!("run_command called with non-tuple argument")
|
unreachable!("run_command called with non-tuple argument")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ impl Config {
|
|||||||
unreachable!("get called with less than 2 args")
|
unreachable!("get called with less than 2 args")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ impl Config {
|
|||||||
unreachable!("for_each called on non-tuple")
|
unreachable!("for_each called on non-tuple")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -133,6 +134,7 @@ impl Config {
|
|||||||
Ok(Type::new(IterT::new(ItersT::Enumerate, data)?))
|
Ok(Type::new(IterT::new(ItersT::Enumerate, data)?))
|
||||||
}),
|
}),
|
||||||
run: Arc::new(|a, _i| Data::new(Iter(Iters::Enumerate, a.clone()))),
|
run: Arc::new(|a, _i| Data::new(Iter(Iters::Enumerate, a.clone()))),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -203,6 +205,7 @@ fn genfunc_iter_and_arg<T: MersType, D: MersData>(
|
|||||||
unreachable!("{name} called on non-tuple")
|
unreachable!("{name} called on non-tuple")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,5 +411,6 @@ fn genfunc_iter_in_val_out(
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
run: Arc::new(run),
|
run: Arc::new(run),
|
||||||
|
inner_statements: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ impl Config {
|
|||||||
};
|
};
|
||||||
o
|
o
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var(
|
.add_var(
|
||||||
"pop".to_string(),
|
"pop".to_string(),
|
||||||
@ -140,6 +141,7 @@ impl Config {
|
|||||||
None => Data::empty_tuple(),
|
None => Data::empty_tuple(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -202,6 +204,7 @@ impl Config {
|
|||||||
.push(Arc::new(RwLock::new(tuple.0[1].clone())));
|
.push(Arc::new(RwLock::new(tuple.0[1].clone())));
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -225,6 +228,7 @@ impl Config {
|
|||||||
unreachable!("as_list called on non-iterable")
|
unreachable!("as_list called on non-iterable")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,8 @@ impl Config {
|
|||||||
} else {
|
} else {
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("parse_int".to_string(), Data::new(data::function::Function {
|
})).add_var("parse_int".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(program::run::Info::neverused()),
|
info: Arc::new(program::run::Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||||
@ -95,7 +96,8 @@ impl Config {
|
|||||||
} else {
|
} else {
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("signum".to_string(), Data::new(data::function::Function {
|
})).add_var("signum".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(program::run::Info::neverused()),
|
info: Arc::new(program::run::Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||||
@ -118,7 +120,8 @@ impl Config {
|
|||||||
} else { 0
|
} else { 0
|
||||||
}
|
}
|
||||||
} else { unreachable!("called signum on non-number type")}))
|
} else { unreachable!("called signum on non-number type")}))
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
})) .add_var("div".to_string(), Data::new(data::function::Function {
|
})) .add_var("div".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(program::run::Info::neverused()),
|
info: Arc::new(program::run::Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||||
@ -137,6 +140,7 @@ impl Config {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
} else { unreachable!() }),
|
} else { unreachable!() }),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("modulo".to_string(), Data::new(data::function::Function {
|
})).add_var("modulo".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(program::run::Info::neverused()),
|
info: Arc::new(program::run::Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||||
@ -155,6 +159,7 @@ impl Config {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
} else { unreachable!() }),
|
} else { unreachable!() }),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var(
|
.add_var(
|
||||||
"sum".to_string(),
|
"sum".to_string(),
|
||||||
@ -215,6 +220,7 @@ impl Config {
|
|||||||
unreachable!("sum called on non-tuple")
|
unreachable!("sum called on non-tuple")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -288,6 +294,7 @@ impl Config {
|
|||||||
unreachable!("sum called on non-tuple")
|
unreachable!("sum called on non-tuple")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -349,6 +356,7 @@ impl Config {
|
|||||||
unreachable!("product called on non-tuple")
|
unreachable!("product called on non-tuple")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -428,6 +436,7 @@ fn ltgtoe_function(
|
|||||||
}
|
}
|
||||||
Data::new(data::bool::Bool(true))
|
Data::new(data::bool::Bool(true))
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -58,6 +58,7 @@ impl Config {
|
|||||||
unreachable!("thread called, but arg wasn't a function");
|
unreachable!("thread called, but arg wasn't a function");
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var("thread_finished".to_string(), Data::new(data::function::Function {
|
.add_var("thread_finished".to_string(), Data::new(data::function::Function {
|
||||||
@ -78,7 +79,8 @@ impl Config {
|
|||||||
Ok(t) => t.is_finished(),
|
Ok(t) => t.is_finished(),
|
||||||
Err(_d) => true,
|
Err(_d) => true,
|
||||||
}))
|
}))
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
.add_var("thread_await".to_string(), Data::new(data::function::Function {
|
.add_var("thread_await".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(program::run::Info::neverused()),
|
info: Arc::new(program::run::Info::neverused()),
|
||||||
@ -103,7 +105,8 @@ impl Config {
|
|||||||
};
|
};
|
||||||
*t = Err(d.clone());
|
*t = Err(d.clone());
|
||||||
d
|
d
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ impl Config {
|
|||||||
_ = std::io::stdin().read_line(&mut line);
|
_ = std::io::stdin().read_line(&mut line);
|
||||||
Data::new(data::string::String(line))
|
Data::new(data::string::String(line))
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -55,6 +56,7 @@ impl Config {
|
|||||||
eprintln!("{} :: {}", a.as_type(), a);
|
eprintln!("{} :: {}", a.as_type(), a);
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -68,6 +70,7 @@ impl Config {
|
|||||||
_ = std::io::stderr().lock().flush();
|
_ = std::io::stderr().lock().flush();
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -80,6 +83,7 @@ impl Config {
|
|||||||
eprintln!("{}", a.get());
|
eprintln!("{}", a.get());
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -93,6 +97,7 @@ impl Config {
|
|||||||
_ = std::io::stdout().lock().flush();
|
_ = std::io::stdout().lock().flush();
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
@ -105,6 +110,7 @@ impl Config {
|
|||||||
println!("{}", a.get());
|
println!("{}", a.get());
|
||||||
Data::empty_tuple()
|
Data::empty_tuple()
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ impl Config {
|
|||||||
}),
|
}),
|
||||||
run: Arc::new(|a, _i| {
|
run: Arc::new(|a, _i| {
|
||||||
Data::new(data::string::String(a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.trim().to_owned()))
|
Data::new(data::string::String(a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.trim().to_owned()))
|
||||||
})
|
}),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("concat".to_string(), Data::new(data::function::Function {
|
})).add_var("concat".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::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())
|
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()))),
|
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 {
|
})).add_var("to_string".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||||
out: Arc::new(|_a, _i| Ok(Type::new(data::string::StringT))),
|
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()))),
|
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 {
|
})).add_var("index_of".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::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())
|
Err(format!("wrong args for index_of: must be (string, string)").into())
|
||||||
}),
|
}),
|
||||||
run: Arc::new(|a, _i| index_of(a, false)),
|
run: Arc::new(|a, _i| index_of(a, false)),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("index_of_rev".to_string(), Data::new(data::function::Function {
|
})).add_var("index_of_rev".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::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())
|
Err(format!("wrong args for index_of: must be (string, string)").into())
|
||||||
}),
|
}),
|
||||||
run: Arc::new(|a, _i| index_of(a, true)),
|
run: Arc::new(|a, _i| index_of(a, true)),
|
||||||
|
inner_statements: None,
|
||||||
})).add_var("substring".to_string(), Data::new(data::function::Function {
|
})).add_var("substring".to_string(), Data::new(data::function::Function {
|
||||||
info: Arc::new(Info::neverused()),
|
info: Arc::new(Info::neverused()),
|
||||||
info_check: Arc::new(Mutex::new(CheckInfo::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()))
|
Data::new(data::string::String(s[start..end].to_owned()))
|
||||||
|
|
||||||
}),
|
}),
|
||||||
|
inner_statements: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,4 +35,10 @@ impl MersStatement for AsType {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,10 @@ impl MersStatement for AssignTo {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,10 @@ impl MersStatement for Block {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,10 @@ impl MersStatement for Chain {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,16 @@ impl MersStatement for CustomType {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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 {
|
impl Debug for CustomType {
|
||||||
|
@ -29,8 +29,10 @@ impl MersStatement for Function {
|
|||||||
let arg_target = Arc::new(self.arg.compile(info, comp)?);
|
let arg_target = Arc::new(self.arg.compile(info, comp)?);
|
||||||
comp.is_init = false;
|
comp.is_init = false;
|
||||||
let run = Arc::new(self.run.compile(info, comp)?);
|
let run = Arc::new(self.run.compile(info, comp)?);
|
||||||
let arg2: Arc<Box<dyn crate::program::run::MersStatement>> = Arc::clone(&arg_target);
|
let arg2 = Arc::clone(&arg_target);
|
||||||
let run2: Arc<Box<dyn crate::program::run::MersStatement>> = Arc::clone(&run);
|
let run2 = Arc::clone(&run);
|
||||||
|
let arg3 = Arc::clone(&arg_target);
|
||||||
|
let run3 = Arc::clone(&run);
|
||||||
Ok(Box::new(program::run::function::Function {
|
Ok(Box::new(program::run::function::Function {
|
||||||
pos_in_src: self.pos_in_src.clone(),
|
pos_in_src: self.pos_in_src.clone(),
|
||||||
func_no_info: data::function::Function {
|
func_no_info: data::function::Function {
|
||||||
@ -44,10 +46,17 @@ impl MersStatement for Function {
|
|||||||
data::defs::assign(&arg, &arg_target.run(info));
|
data::defs::assign(&arg, &arg_target.run(info));
|
||||||
run.run(info)
|
run.run(info)
|
||||||
}),
|
}),
|
||||||
|
inner_statements: Some((arg3, run3)),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,4 +36,18 @@ impl MersStatement for If {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ impl MersStatement for IncludeMers {
|
|||||||
info_check: Arc::new(Mutex::new(info::Info::neverused())),
|
info_check: Arc::new(Mutex::new(info::Info::neverused())),
|
||||||
out: Arc::new(move |_, i| compiled.check(&mut i.duplicate(), None)),
|
out: Arc::new(move |_, i| compiled.check(&mut i.duplicate(), None)),
|
||||||
run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())),
|
run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())),
|
||||||
|
inner_statements: None,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
as_part_of_include: Some(self.inner_src.clone()),
|
as_part_of_include: Some(self.inner_src.clone()),
|
||||||
@ -66,4 +67,10 @@ impl MersStatement for IncludeMers {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
self.pos_in_src.clone()
|
||||||
}
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,10 @@ impl MersStatement for InitTo {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
use std::{collections::HashMap, fmt::Debug};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Debug,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{CheckError, SourceRange},
|
errors::{CheckError, SourceRange},
|
||||||
@ -44,21 +48,53 @@ pub trait MersStatement: Debug + Send + Sync {
|
|||||||
info: &mut Info,
|
info: &mut Info,
|
||||||
comp: CompInfo,
|
comp: CompInfo,
|
||||||
) -> Result<Box<dyn super::run::MersStatement>, CheckError> {
|
) -> Result<Box<dyn super::run::MersStatement>, CheckError> {
|
||||||
|
info.global.depth += 1;
|
||||||
if self.has_scope() {
|
if self.has_scope() {
|
||||||
info.create_scope();
|
info.create_scope();
|
||||||
}
|
}
|
||||||
let o = self.compile_custom(info, comp);
|
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() {
|
if self.has_scope() {
|
||||||
info.end_scope();
|
info.end_scope();
|
||||||
}
|
}
|
||||||
|
info.global.depth -= 1;
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
fn source_range(&self) -> SourceRange;
|
fn source_range(&self) -> SourceRange;
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement>;
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct CompInfo {
|
pub struct CompInfo {
|
||||||
is_init: bool,
|
pub is_init: bool,
|
||||||
}
|
}
|
||||||
impl Default for CompInfo {
|
impl Default for CompInfo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -70,13 +106,30 @@ pub type Info = info::Info<Local>;
|
|||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
vars: HashMap<String, (usize, usize)>,
|
pub vars: HashMap<String, (usize, usize)>,
|
||||||
vars_count: usize,
|
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<Info>, Result<(), CheckError>)>,
|
||||||
|
usize,
|
||||||
|
usize,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
impl info::Local for Local {
|
impl info::Local for Local {
|
||||||
type VariableIdentifier = String;
|
type VariableIdentifier = String;
|
||||||
type VariableData = (usize, usize);
|
type VariableData = (usize, usize);
|
||||||
type Global = ();
|
type Global = LocalGlobalInfo;
|
||||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
||||||
self.vars_count += 1;
|
self.vars_count += 1;
|
||||||
self.vars.insert(id, value);
|
self.vars.insert(id, value);
|
||||||
|
@ -32,4 +32,10 @@ impl MersStatement for Object {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,10 @@ impl MersStatement for Tuple {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,4 +29,10 @@ impl MersStatement for Value {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
self.pos_in_src.clone()
|
||||||
}
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,4 +59,10 @@ impl MersStatement for Variable {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
self.pos_in_src.clone()
|
||||||
}
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,4 +75,10 @@ impl MersStatement for AsType {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,4 +83,10 @@ impl MersStatement for AssignTo {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,10 @@ impl MersStatement for Block {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,4 +101,10 @@ impl MersStatement for Chain {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,12 @@ impl MersStatement for CustomType {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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 {
|
impl Debug for CustomType {
|
||||||
|
@ -34,4 +34,14 @@ impl MersStatement for Function {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,4 +73,18 @@ impl MersStatement for If {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, Mutex, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -43,13 +43,50 @@ pub trait MersStatement: Debug + Send + Sync {
|
|||||||
/// if true, local variables etc. will be contained inside their own scope.
|
/// if true, local variables etc. will be contained inside their own scope.
|
||||||
fn has_scope(&self) -> bool;
|
fn has_scope(&self) -> bool;
|
||||||
fn check(&self, info: &mut CheckInfo, assign: Option<&Type>) -> Result<Type, CheckError> {
|
fn check(&self, info: &mut CheckInfo, assign: Option<&Type>) -> Result<Type, CheckError> {
|
||||||
|
info.global.depth += 1;
|
||||||
if self.has_scope() {
|
if self.has_scope() {
|
||||||
info.create_scope();
|
info.create_scope();
|
||||||
}
|
}
|
||||||
let o = self.check_custom(info, assign);
|
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() {
|
if self.has_scope() {
|
||||||
info.end_scope();
|
info.end_scope();
|
||||||
}
|
}
|
||||||
|
info.global.depth -= 1;
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
fn run(&self, info: &mut Info) -> Data {
|
fn run(&self, info: &mut Info) -> Data {
|
||||||
@ -63,6 +100,8 @@ pub trait MersStatement: Debug + Send + Sync {
|
|||||||
o
|
o
|
||||||
}
|
}
|
||||||
fn source_range(&self) -> SourceRange;
|
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<Local>;
|
pub type Info = info::Info<Local>;
|
||||||
@ -70,11 +109,11 @@ pub type CheckInfo = info::Info<CheckLocal>;
|
|||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
vars: Vec<Arc<RwLock<Data>>>,
|
pub vars: Vec<Arc<RwLock<Data>>>,
|
||||||
}
|
}
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct CheckLocal {
|
pub struct CheckLocal {
|
||||||
vars: Vec<Type>,
|
pub vars: Vec<Type>,
|
||||||
pub types: HashMap<
|
pub types: HashMap<
|
||||||
String,
|
String,
|
||||||
Result<
|
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<CheckInfo>, Result<Type, CheckError>)>,
|
||||||
|
usize,
|
||||||
|
usize,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
}
|
||||||
impl Debug for CheckLocal {
|
impl Debug for CheckLocal {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys())
|
write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys())
|
||||||
@ -124,7 +180,7 @@ impl info::Local for Local {
|
|||||||
impl info::Local for CheckLocal {
|
impl info::Local for CheckLocal {
|
||||||
type VariableIdentifier = usize;
|
type VariableIdentifier = usize;
|
||||||
type VariableData = Type;
|
type VariableData = Type;
|
||||||
type Global = ();
|
type Global = CheckLocalGlobalInfo;
|
||||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
||||||
while self.vars.len() <= id {
|
while self.vars.len() <= id {
|
||||||
self.vars.push(Type::empty());
|
self.vars.push(Type::empty());
|
||||||
|
@ -133,4 +133,10 @@ impl MersStatement for Object {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,4 +93,10 @@ impl MersStatement for Tuple {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,10 @@ impl MersStatement for Value {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
self.pos_in_src.clone()
|
||||||
}
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,4 +79,10 @@ impl MersStatement for Variable {
|
|||||||
fn source_range(&self) -> SourceRange {
|
fn source_range(&self) -> SourceRange {
|
||||||
self.pos_in_src.clone()
|
self.pos_in_src.clone()
|
||||||
}
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user