use crate::debug_trace;
use crate::engine::grammar::SPG;
use crate::engine::parse::TypedParser;
use crate::engine::structure::ast::FusionAST;
use crate::semantics::TypingRuntime;
use crate::typing::{Context, TypingDomain};
#[cfg(test)]
mod tests;
pub struct Synthesizer {
spg: SPG,
runtime: TypingRuntime,
parser: TypedParser,
input: String,
ctx: Context,
tree: Option<FusionAST>,
}
impl Synthesizer {
pub fn new(spg: SPG, input: impl Into<String>) -> Self {
let input = input.into();
debug_trace!("synth", "new: input='{}'", input);
let runtime = TypingRuntime::new(TypingDomain::default(), spg.clone());
let parser = TypedParser::new(spg.clone(), runtime.clone());
Self {
spg,
runtime,
parser,
ctx: Context::new(),
input,
tree: None,
}
}
pub fn grammar(&self) -> &SPG {
&self.spg
}
pub fn runtime(&self) -> &TypingRuntime {
&self.runtime
}
pub fn ctx(&self) -> &Context {
&self.ctx
}
pub fn with_ctx(&mut self, ctx: Context) {
self.ctx = ctx;
self.tree = None;
let _ = self.ast();
}
pub fn input(&self) -> &str {
&self.input
}
pub fn set_input(&mut self, input: impl Into<String>) {
self.input = input.into();
self.tree = None;
let _ = self.ast();
}
pub fn ast(&mut self) -> Result<FusionAST, String> {
if let Some(ast) = &self.tree {
Ok(ast.clone())
} else {
let ctx_id = self.runtime.intern_context(self.ctx.clone());
match self.parser.parse(&self.input, ctx_id) {
Ok(ast) => {
debug_trace!("synth", "ast: input='{}' parsed successfully", self.input);
self.tree = Some(ast.clone());
Ok(ast)
}
Err(err) => {
debug_trace!("synth", "ast: input='{}' parse failed: {}", self.input, err);
Err(format!("Parse error: {err}"))
}
}
}
}
pub fn parse_with(&mut self, ctx: &Context) -> Result<FusionAST, String> {
self.with_ctx(ctx.clone());
self.ast()
}
pub fn feed_with(&mut self, token: &str, ctx: &Context) -> Result<FusionAST, String> {
self.with_ctx(ctx.clone());
self.feed(token)
}
pub fn feed(&mut self, token: &str) -> Result<FusionAST, String> {
debug_trace!("synth", "feed: input='{}' token='{}'", self.input, token);
let extended = format!("{}{}", self.input, token);
self.set_input(extended);
self.ast()
}
#[must_use = "discarding try_feed result hides parse failures"]
pub fn try_feed(&mut self, token: &str) -> Result<FusionAST, String> {
debug_trace!("synth", "try: input='{}' token='{}'", self.input, token);
let extended = format!("{}{}", self.input, token);
let mut p = self.parser.fork();
let ctx_id = self.runtime.intern_context(self.ctx.clone());
match p.parse(&extended, ctx_id) {
Ok(ast) => Ok(ast),
Err(err) => Err(format!("try_feed failed: {err}")),
}
}
}