From aca39eff18ff2ad202387680e1c3088ec3b85ddb Mon Sep 17 00:00:00 2001 From: "Jakub A. G" Date: Tue, 7 May 2024 10:33:28 +0200 Subject: [PATCH] Allow other formats for singleton input (#1901) (#1902) Guess input format based on extension similarly like in multiple input case. --- core/src/cache.rs | 65 ++++++++++++--------------------------- core/src/program.rs | 6 ++-- core/src/repl/mod.rs | 6 ++-- lsp/nls/src/incomplete.rs | 4 +-- lsp/nls/src/world.rs | 4 +-- utils/src/bench.rs | 4 +-- 6 files changed, 33 insertions(+), 56 deletions(-) diff --git a/core/src/cache.rs b/core/src/cache.rs index 0a7ccde0..5c535d42 100644 --- a/core/src/cache.rs +++ b/core/src/cache.rs @@ -58,6 +58,14 @@ impl InputFormat { _ => None, } } + /// Renturns an [InputFormat] based on the extension of a source path. + pub fn from_source_path(source_path: &SourcePath) -> Option { + if let SourcePath::Path(p) = source_path { + Self::from_path(p) + } else { + None + } + } } /// File and terms cache. @@ -456,50 +464,10 @@ impl Cache { } } - /// Parse a source and populate the corresponding entry in the cache, or do nothing if the - /// entry has already been parsed. This function is error tolerant: parts of the source which - /// result in parse errors are parsed as [`crate::term::Term::ParseError`] and the - /// corresponding error messages are collected and returned. - /// - /// The `Err` part of the result corresponds to non-recoverable errors. - fn parse_lax(&mut self, file_id: FileId) -> Result, ParseError> { - if let Some(TermEntry { parse_errs, .. }) = self.terms.get(&file_id) { - Ok(CacheOp::Cached(parse_errs.clone())) - } else { - let (term, parse_errs) = self.parse_nocache(file_id)?; - self.terms.insert( - file_id, - TermEntry { - term, - state: EntryState::Parsed, - parse_errs: parse_errs.clone(), - }, - ); - - Ok(CacheOp::Done(parse_errs)) - } - } - - /// Parse a source and populate the corresponding entry in the cache, or do - /// nothing if the entry has already been parsed. This function is error - /// tolerant if `self.error_tolerant` is `true`. - pub fn parse(&mut self, file_id: FileId) -> Result, ParseErrors> { - let result = self.parse_lax(file_id); - - match self.error_tolerance { - ErrorTolerance::Tolerant => result.map_err(|err| err.into()), - ErrorTolerance::Strict => match result? { - CacheOp::Done(e) | CacheOp::Cached(e) if !e.no_errors() => Err(e), - CacheOp::Done(_) => Ok(CacheOp::Done(ParseErrors::none())), - CacheOp::Cached(_) => Ok(CacheOp::Cached(ParseErrors::none())), - }, - } - } - /// Parse a source and populate the corresponding entry in the cache, or do /// nothing if the entry has already been parsed. Support multiple formats. /// This function is always error tolerant, independently from `self.error_tolerant`. - fn parse_multi_lax( + fn parse_lax( &mut self, file_id: FileId, format: InputFormat, @@ -523,12 +491,12 @@ impl Cache { /// Parse a source and populate the corresponding entry in the cache, or do /// nothing if the entry has already been parsed. Support multiple formats. /// This function is error tolerant if `self.error_tolerant` is `true`. - pub fn parse_multi( + pub fn parse( &mut self, file_id: FileId, format: InputFormat, ) -> Result, ParseErrors> { - let result = self.parse_multi_lax(file_id, format); + let result = self.parse_lax(file_id, format); match self.error_tolerance { ErrorTolerance::Tolerant => result.map_err(|err| err.into()), @@ -902,7 +870,12 @@ impl Cache { ) -> Result, Error> { let mut result = CacheOp::Cached(()); - if let CacheOp::Done(_) = self.parse(file_id)? { + let format = self + .file_paths + .get(&file_id) + .and_then(InputFormat::from_source_path) + .unwrap_or_default(); + if let CacheOp::Done(_) = self.parse(file_id, format)? { result = CacheOp::Done(()); } @@ -1134,7 +1107,7 @@ impl Cache { .collect(); for (_, file_id) in file_ids.iter() { - self.parse(*file_id)?; + self.parse(*file_id, InputFormat::Nickel)?; } self.stdlib_ids.replace(file_ids); Ok(CacheOp::Done(())) @@ -1371,7 +1344,7 @@ impl ImportResolver for Cache { self.rev_imports.entry(file_id).or_default().insert(parent); } - self.parse_multi(file_id, format) + self.parse(file_id, format) .map_err(|err| ImportError::ParseErrors(err, *pos))?; Ok((result, file_id)) diff --git a/core/src/program.rs b/core/src/program.rs index ace45646..8e8c01e3 100644 --- a/core/src/program.rs +++ b/core/src/program.rs @@ -371,7 +371,7 @@ impl Program { pub fn parse(&mut self) -> Result { self.vm .import_resolver_mut() - .parse(self.main_id) + .parse(self.main_id, InputFormat::Nickel) .map_err(Error::ParseErrors)?; Ok(self .vm @@ -496,7 +496,9 @@ impl Program { /// Load, parse, and typecheck the program and the standard library, if not already done. pub fn typecheck(&mut self) -> Result<(), Error> { - self.vm.import_resolver_mut().parse(self.main_id)?; + self.vm + .import_resolver_mut() + .parse(self.main_id, InputFormat::Nickel)?; self.vm.import_resolver_mut().load_stdlib()?; let initial_env = self.vm.import_resolver().mk_type_ctxt().expect( "program::typecheck(): \ diff --git a/core/src/repl/mod.rs b/core/src/repl/mod.rs index a06bc6aa..1e376a10 100644 --- a/core/src/repl/mod.rs +++ b/core/src/repl/mod.rs @@ -6,7 +6,7 @@ //! Dually, the frontend is the user-facing part, which may be a CLI, a web application, a //! jupyter-kernel (which is not exactly user-facing, but still manages input/output and //! formatting), etc. -use crate::cache::{Cache, Envs, ErrorTolerance, SourcePath}; +use crate::cache::{Cache, Envs, ErrorTolerance, InputFormat, SourcePath}; use crate::error::{ report::{self, ColorOpt, ErrorFormat}, Error, EvalError, IOError, IntoDiagnostics, ParseError, ParseErrors, ReplError, @@ -232,7 +232,9 @@ impl Repl for ReplImpl { .import_resolver_mut() .add_file(OsString::from(path.as_ref())) .map_err(IOError::from)?; - self.vm.import_resolver_mut().parse(file_id)?; + self.vm + .import_resolver_mut() + .parse(file_id, InputFormat::Nickel)?; let term = self.vm.import_resolver().get_owned(file_id).unwrap(); let pos = term.pos; diff --git a/lsp/nls/src/incomplete.rs b/lsp/nls/src/incomplete.rs index 0990eab5..92f4af2f 100644 --- a/lsp/nls/src/incomplete.rs +++ b/lsp/nls/src/incomplete.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use nickel_lang_core::{ - cache::SourcePath, + cache::{InputFormat, SourcePath}, parser::lexer::{self, NormalToken, SpannedToken, Token}, position::RawSpan, term::{RichTerm, Term}, @@ -102,7 +102,7 @@ fn resolve_imports(rt: RichTerm, world: &mut World) -> RichTerm { } = import_resolution::tolerant::resolve_imports(rt, &mut world.cache); for id in resolved_ids { - if world.cache.parse(id).is_ok() { + if world.cache.parse(id, InputFormat::Nickel).is_ok() { // If a new input got imported in an incomplete term, try to typecheck // (and build lookup tables etc.) for it, but don't issue diagnostics. let _ = world.typecheck(id); diff --git a/lsp/nls/src/world.rs b/lsp/nls/src/world.rs index ddf93f76..092a4944 100644 --- a/lsp/nls/src/world.rs +++ b/lsp/nls/src/world.rs @@ -8,7 +8,7 @@ use codespan::FileId; use lsp_server::{ErrorCode, ResponseError}; use lsp_types::Url; use nickel_lang_core::{ - cache::{Cache, CacheError, ErrorTolerance, SourcePath}, + cache::{Cache, CacheError, ErrorTolerance, InputFormat, SourcePath}, error::{ImportError, IntoDiagnostics}, position::{RawPos, RawSpan}, term::{record::FieldMetadata, RichTerm, Term, UnaryOp}, @@ -160,7 +160,7 @@ impl World { file_id: FileId, ) -> Result, Vec> { self.cache - .parse(file_id) + .parse(file_id, InputFormat::Nickel) .map(|nonfatal| self.lsp_diagnostics(file_id, nonfatal.inner())) .map_err(|fatal| self.lsp_diagnostics(file_id, fatal)) } diff --git a/utils/src/bench.rs b/utils/src/bench.rs index 5aff8e52..b2759491 100644 --- a/utils/src/bench.rs +++ b/utils/src/bench.rs @@ -171,7 +171,7 @@ macro_rules! ncl_bench_group { (name = $group_name:ident; config = $config:expr; $($b:tt),+ $(,)*) => { pub fn $group_name() { use nickel_lang_core::{ - cache::{Envs, Cache, ErrorTolerance, ImportResolver}, + cache::{Envs, Cache, ErrorTolerance, ImportResolver, InputFormat}, eval::{VirtualMachine, cache::{CacheImpl, Cache as EvalCache}}, transform::import_resolution::strict::resolve_imports, error::report::{report, ColorOpt, ErrorFormat}, @@ -194,7 +194,7 @@ macro_rules! ncl_bench_group { .unwrap() .transformed_term; if bench.eval_mode == $crate::bench::EvalMode::TypeCheck { - cache.parse(id).unwrap(); + cache.parse(id, InputFormat::Nickel).unwrap(); cache.resolve_imports(id).unwrap(); } (cache, id, t)