use alloc::{boxed::Box, format, vec::Vec};
use core::str::FromStr;
use diag_lang::{Diagnostic, Label, Severity};
use pass_lang::{Outcome, Pass, PassError, PassManager};
use syntax_lang::{Span, Token};
use crate::{
Error, Parse,
error::Report,
grammar::{self, Grammar},
kind::Kind,
noml, parser, schematic,
};
pub type Capability = Box<dyn for<'a> Pass<Parse<'a>>>;
#[derive(Clone, Debug)]
pub struct Language {
grammar: Grammar,
}
impl Language {
pub fn from_lsf(schematic: &str) -> Result<Self, Error> {
let mut report = Report::default();
let root = match noml::read(schematic) {
Ok(root) => root,
Err(diagnostic) => {
report.diagnostic(diagnostic);
return Err(report.into_error(schematic));
}
};
let spec = schematic::interpret(root, &mut report);
let grammar = spec.and_then(|spec| grammar::compile(&spec, schematic, &mut report));
match grammar {
Some(grammar) if report.is_clean() => Ok(Self { grammar }),
_ => {
if report.is_clean() {
report.error(Span::empty(0), "the schematic could not be forged");
}
Err(report.into_error(schematic))
}
}
}
#[cfg(test)]
pub(crate) fn grammar(&self) -> &Grammar {
&self.grammar
}
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.grammar.name
}
#[inline]
#[must_use]
pub fn version(&self) -> Option<&str> {
self.grammar.version.as_deref()
}
pub fn extensions(&self) -> impl ExactSizeIterator<Item = &str> {
self.grammar.extensions.iter().map(|e| &**e)
}
pub fn capabilities(&self) -> impl ExactSizeIterator<Item = &str> {
self.grammar.capabilities.iter().map(|c| &*c.name)
}
#[must_use]
pub fn kind(&self, name: &str) -> Option<Kind> {
self.grammar.kinds.get(name)
}
#[must_use]
pub fn kind_name(&self, kind: Kind) -> &str {
self.grammar.kinds.name(kind)
}
#[must_use]
pub fn lex(&self, source: &str) -> Vec<Token<Kind>> {
let mut tokens = Vec::new();
if u32::try_from(source.len()).is_ok() {
let mut diagnostics = Vec::new();
self.grammar
.lexer
.run(source, &mut tokens, &mut diagnostics);
}
tokens
}
#[must_use]
pub fn parse<'a>(&'a self, source: &'a str) -> Parse<'a> {
let (tree, diagnostics) = parser::parse(&self.grammar, source);
Parse::new(self, source, tree, diagnostics)
}
pub fn pipeline<'a>(
&self,
passes: impl IntoIterator<Item = Capability>,
) -> Result<PassManager<Parse<'a>>, Error> {
let mut available: Vec<Option<Capability>> = passes.into_iter().map(Some).collect();
let mut manager = PassManager::new();
let mut problems = Vec::new();
let mut location = None;
for capability in self.grammar.capabilities.iter() {
let mut matching = available
.iter()
.enumerate()
.filter(|(_, p)| p.as_ref().is_some_and(|p| p.name() == &*capability.name))
.map(|(i, _)| i);
let (first, second) = (matching.next(), matching.next());
let problem = match (first, second) {
(Some(i), None) => {
if let Some(pass) = available[i].take() {
let _ = manager.add(Plugged(pass));
}
continue;
}
(None, _) => Diagnostic::new(
Severity::Error,
format!("capability `{}` has no pass", capability.name),
Label::unlabelled(capability.span),
)
.with_help(format!(
"pass a capability whose `name()` is \"{}\"",
capability.name
)),
(Some(_), Some(_)) => Diagnostic::new(
Severity::Error,
format!("capability `{}` has more than one pass", capability.name),
Label::unlabelled(capability.span),
),
};
if location.is_none() {
location = Some((capability.line, capability.column));
}
problems.push(problem);
}
match location {
None => Ok(manager),
Some((line, column)) => Err(Error::located(problems, line, column)),
}
}
}
impl FromStr for Language {
type Err = Error;
fn from_str(schematic: &str) -> Result<Self, Error> {
Self::from_lsf(schematic)
}
}
struct Plugged(Capability);
impl<'a> Pass<Parse<'a>> for Plugged {
fn name(&self) -> &'static str {
self.0.name()
}
fn run(&mut self, unit: &mut Parse<'a>) -> Result<Outcome, PassError> {
self.0.run(unit)
}
}