oden/fine/src/lib.rs
John Doty 994268abb6 [fine] Oh no a runtime and module loading and stuff
Lots of test work to use the new mechanism. I'm not sure I like it.
2024-02-12 22:49:34 -08:00

167 lines
4.7 KiB
Rust

use std::{collections::HashMap, fs, rc::Rc};
use compiler::compile;
use parser::parse;
use semantics::{check, Error, Semantics};
use vm::{eval, Context};
pub mod compiler;
pub mod parser;
pub mod semantics;
pub mod tokens;
pub mod vm;
pub enum ModuleSource {
SourceText(String),
}
#[derive(Debug)]
pub enum ModuleLoadError {
IO(std::io::Error),
}
pub trait ModuleLoader {
fn normalize_module_name(&self, name: String) -> String;
fn load_module(&self, name: &String) -> Result<ModuleSource, ModuleLoadError>;
}
pub struct StandardModuleLoader {}
impl ModuleLoader for StandardModuleLoader {
fn normalize_module_name(&self, name: String) -> String {
match std::fs::canonicalize(&name) {
Ok(p) => match p.into_os_string().into_string() {
Ok(s) => s,
Err(_) => name,
},
Err(_) => name,
}
}
fn load_module(&self, name: &String) -> Result<ModuleSource, ModuleLoadError> {
match fs::read_to_string(name) {
Ok(c) => Ok(ModuleSource::SourceText(c)),
Err(e) => Err(ModuleLoadError::IO(e)),
}
}
}
pub struct Module {
module: Rc<compiler::Module>,
semantics: Rc<Semantics>,
}
impl Module {
pub fn semantics(&self) -> Rc<Semantics> {
self.semantics.clone()
}
pub fn compiled(&self) -> Rc<compiler::Module> {
self.module.clone()
}
}
pub struct Runtime {
modules: HashMap<String, Rc<Module>>,
loader: Box<dyn ModuleLoader>,
}
impl Runtime {
pub fn new(loader: Box<dyn ModuleLoader>) -> Self {
Runtime {
modules: HashMap::new(),
loader,
}
}
pub fn load_module(&mut self, name: &str) -> Result<(Vec<Error>, Rc<Module>), ModuleLoadError> {
let mut init_pending = HashMap::new();
let mut names = Vec::new();
let name = self.loader.normalize_module_name(name.to_string());
names.push(name.clone());
while let Some(name) = names.pop() {
if self.modules.contains_key(&name) {
continue;
}
if !init_pending.contains_key(&name) {
let loaded = self.loader.load_module(&name)?;
match loaded {
ModuleSource::SourceText(source) => {
let source: Rc<str> = source.into();
let (tree, lines) = parse(&source);
let semantics = Rc::new(Semantics::new(source, tree, lines));
let mut normalized = Vec::new();
for import in semantics.imports() {
let import = self.loader.normalize_module_name(import);
names.push(import.clone());
normalized.push(import);
}
init_pending.insert(name, (normalized, semantics));
}
}
}
}
for (_, (imports, semantics)) in init_pending.iter() {
let mut import_table = HashMap::new();
for import in imports.iter() {
let target = if let Some(module) = self.modules.get(&*import) {
Rc::downgrade(&module.semantics)
} else {
Rc::downgrade(&init_pending.get(&*import).unwrap().1)
};
import_table.insert(import.clone(), target);
}
semantics.set_imports(import_table);
}
let mut errors = Vec::new();
for (name, (_, semantics)) in init_pending.into_iter() {
check(&semantics);
errors.append(&mut semantics.snapshot_errors());
let module = compile(&semantics);
self.modules
.insert(name, Rc::new(Module { semantics, module }));
}
let result = self.modules.get(&name).unwrap().clone();
Ok((errors, result))
}
}
pub fn process_file(file: &str) {
let mut runtime = Runtime::new(Box::new(StandardModuleLoader {}));
let (errors, module) = match runtime.load_module(file) {
Ok(r) => r,
Err(_) => {
eprintln!("Error loading module");
return;
}
};
// OK now there might be errors.
if errors.len() > 0 {
for e in errors {
eprintln!("{file}: {}:{}: {}", e.start.0, e.start.1, e.message);
}
return;
}
// shrug
let module = module.module.clone();
let main_function = module.functions[module.init].clone();
let mut context = Context::new(module.clone());
match eval(&mut context, main_function, vec![]) {
Ok(v) => {
println!("{:?}", v);
}
Err(e) => {
eprintln!("{:?}", e);
}
}
}