Implement module loading and imports

This commit is contained in:
2026-06-16 10:01:31 +09:00
parent fead194ba6
commit bd3da1aeee
13 changed files with 586 additions and 124 deletions
+82 -30
View File
@@ -1,6 +1,10 @@
use std::{env, fs, process::ExitCode};
use std::{
env, fs,
path::{Path, PathBuf},
process::ExitCode,
};
use decodal_core::{Data, Diagnostic, Engine, parse_source};
use decodal_core::{Data, Diagnostic, DiagnosticKind, Engine, LoadedSource, SourceLoader, Span};
fn main() -> ExitCode {
match run() {
@@ -26,34 +30,36 @@ fn run() -> Result<(), Diagnostic> {
}
"check" => {
let path = args.next().unwrap_or_else(|| String::from("-"));
let source = read_source(&path)?;
let parsed = parse_source(&source)?;
let mut engine = Engine::from_parse(parsed.ast, parsed.root);
let value = engine.eval_root()?;
engine.materialize(&value)?;
let data = materialize_path(&path)?;
drop(data);
println!("ok");
Ok(())
}
"eval" | "materialize" => {
let path = args.next().unwrap_or_else(|| String::from("-"));
materialize_file(&path)
let data = materialize_path(&path)?;
print_data(&data, 0);
println!();
Ok(())
}
path => {
let data = materialize_path(path)?;
print_data(&data, 0);
println!();
Ok(())
}
path => materialize_file(path),
}
}
fn materialize_file(path: &str) -> Result<(), Diagnostic> {
let source = read_source(path)?;
let parsed = parse_source(&source)?;
let mut engine = Engine::from_parse(parsed.ast, parsed.root);
let value = engine.eval_root()?;
let data = engine.materialize(&value)?;
print_data(&data, 0);
println!();
Ok(())
fn materialize_path(path: &str) -> Result<Data, Diagnostic> {
let root = read_root_source(path)?;
let mut engine = Engine::new(FsLoader);
let module = engine.add_root_source(root.key, root.name, &root.source)?;
let value = engine.eval_module(module)?;
engine.materialize(&value)
}
fn read_source(path: &str) -> Result<String, Diagnostic> {
fn read_root_source(path: &str) -> Result<LoadedSource, Diagnostic> {
if path == "-" {
use std::io::Read;
let mut source = String::new();
@@ -61,23 +67,68 @@ fn read_source(path: &str) -> Result<String, Diagnostic> {
.read_to_string(&mut source)
.map_err(|error| {
Diagnostic::new(
decodal_core::DiagnosticKind::Import,
decodal_core::Span::default(),
DiagnosticKind::Import,
Span::default(),
format!("failed to read stdin: {error}"),
)
})?;
Ok(source)
} else {
fs::read_to_string(path).map_err(|error| {
Diagnostic::new(
decodal_core::DiagnosticKind::Import,
decodal_core::Span::default(),
format!("failed to read `{path}`: {error}"),
)
Ok(LoadedSource {
key: String::from("<stdin>"),
name: String::from("<stdin>"),
source,
})
} else {
load_path(Path::new(path))
}
}
#[derive(Debug, Clone, Copy)]
struct FsLoader;
impl SourceLoader for FsLoader {
fn load(
&mut self,
current_key: Option<&str>,
specifier: &str,
) -> Result<LoadedSource, Diagnostic> {
let path = Path::new(specifier);
let path = if path.is_absolute() {
PathBuf::from(path)
} else if let Some(current_key) = current_key.filter(|key| *key != "<stdin>") {
Path::new(current_key)
.parent()
.unwrap_or_else(|| Path::new("."))
.join(path)
} else {
PathBuf::from(path)
};
load_path(&path)
}
}
fn load_path(path: &Path) -> Result<LoadedSource, Diagnostic> {
let canonical = path.canonicalize().map_err(|error| {
Diagnostic::new(
DiagnosticKind::Import,
Span::default(),
format!("failed to resolve `{}`: {error}", path.display()),
)
})?;
let source = fs::read_to_string(&canonical).map_err(|error| {
Diagnostic::new(
DiagnosticKind::Import,
Span::default(),
format!("failed to read `{}`: {error}", canonical.display()),
)
})?;
let key = canonical.to_string_lossy().into_owned();
Ok(LoadedSource {
name: key.clone(),
key,
source,
})
}
fn print_help() {
println!("Decodal - Deferred Constraint Data Language");
println!();
@@ -90,8 +141,9 @@ fn print_help() {
fn print_diagnostic(error: &Diagnostic) {
eprintln!(
"error[{kind:?}] {start}..{end}: {message}",
"error[{kind:?}] {source}:{start}..{end}: {message}",
kind = error.kind,
source = error.span.source.0,
start = error.span.start,
end = error.span.end,
message = error.message,