use super::{Grammar, Symbol};
use crate::logic::typing::Conclusion;
use std::path::Path;
impl Grammar {
pub fn to_spec_string(&self) -> String {
let mut out = String::new();
let nt_list: Vec<&String> = self.productions.keys().collect();
out.push_str("// --- Production Rules ---\n");
for nt in nt_list {
if let Some(alts) = self.productions.get(nt) {
let mut first = true;
for prod in alts {
let lhs = if let Some(rule_name) = self.nonterminal_rule(nt) {
format!("{}({})", nt, rule_name)
} else {
nt.clone()
};
let rhs = self.format_rhs(&prod.rhs);
if first {
out.push_str(&format!("{} ::= {}", lhs, rhs));
first = false;
} else {
out.push_str(&format!(" | {}", rhs));
}
}
out.push('\n');
}
}
out.push('\n');
if !self.rules.is_empty() {
out.push_str("// --- Typing Rules ---\n");
let mut rule_list: Vec<_> = self.rules.values().collect();
rule_list.sort_by_key(|r| &r.name);
for rule in rule_list {
out.push_str(&format_premises(&rule.premises));
out.push('\n');
let concl_str = format_conclusion(&rule.conclusion);
let line = "-".repeat(std::cmp::max(20, concl_str.len() + 5));
out.push_str(&format!("{} ({})\n", line, rule.name));
out.push_str(&concl_str);
out.push_str("\n\n");
}
}
out
}
fn format_rhs(&self, rhs_symbols: &[Symbol]) -> String {
rhs_symbols
.iter()
.map(|s| self.format_symbol(s))
.collect::<Vec<_>>()
.join(" ")
}
fn format_symbol(&self, symbol: &Symbol) -> String {
match symbol {
Symbol::Nonterminal { name, binding, .. } => {
if let Some(b) = binding {
format!("{}[{}]", name, b)
} else {
name.to_string()
}
}
Symbol::Terminal { regex, binding } => {
let base = format!("/{}/", regex.to_pattern());
if let Some(b) = binding {
format!("{}[{}]", base, b)
} else {
base
}
}
}
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
std::fs::write(path, self.to_spec_string())
}
}
fn format_premises(premises: &[crate::logic::typing::Premise]) -> String {
use crate::logic::typing::TypingJudgment;
premises
.iter()
.map(|p| match (&p.setting, &p.judgment) {
(Some(setting), Some(TypingJudgment::Ascription((term, ty)))) => {
if setting.extensions.is_empty() {
format!("{} ⊢ {} : {}", setting.name, term, ty)
} else {
let exts = setting
.extensions
.iter()
.map(|(v, t)| format!("[{}:{}]", v, t))
.collect::<Vec<_>>()
.join("");
format!("{}{} ⊢ {} : {}", setting.name, exts, term, ty)
}
}
(None, Some(TypingJudgment::Ascription((term, ty)))) => {
format!("{} : {}", term, ty)
}
(None, Some(TypingJudgment::Membership(var, ctx))) => {
format!("{} ∈ {}", var, ctx)
}
(Some(_), Some(TypingJudgment::Membership(var, ctx))) => {
format!("{} ∈ {}", var, ctx)
}
(_, Some(TypingJudgment::Operation { left, op, right })) => {
format!("{} {} {}", left, op, right)
}
(Some(setting), None) => setting.name.to_string(),
(None, None) => String::new(),
})
.collect::<Vec<_>>()
.join(", ")
}
fn format_conclusion(conclusion: &Conclusion) -> String {
format!("{}", conclusion)
}