diff --git a/automata/src/automatan/fa.rs b/automata/src/automatan/fa.rs index ab8cecc..ff7399b 100644 --- a/automata/src/automatan/fa.rs +++ b/automata/src/automatan/fa.rs @@ -173,7 +173,7 @@ impl<'a, 'b> FaCompiler<'a, 'b> { TL::Item(S("F", _), list) => self.compile_final_states(list, span), TL::Item(S(INITIAL_STATE, _), item) => self.compile_initial_state(item, span), TL::Item(S(name, dest_s), _) => { - self.ctx.emit_error(format!("unknown item {name:?}, expected states, alphabet, final states, initial state"), dest_s); + self.ctx.emit_error(format!("unknown item {name:?}, expected states | alphabet | final states | initial state"), dest_s); } TL::TransitionFunc(S((S(delta_lower!(pat), _), args), _), list) => { diff --git a/automata/src/automatan/pda.rs b/automata/src/automatan/pda.rs index 5959ed9..e3c2c02 100644 --- a/automata/src/automatan/pda.rs +++ b/automata/src/automatan/pda.rs @@ -60,12 +60,19 @@ dual_struct_serde! { {#[serde_with::serde_as]} } } +#[derive(Clone, Copy)] +enum AcceptBy { + EmptyStack, + FinalState, +} + pub struct PdaCompiler<'a, 'b> { ctx: &'b mut Context<'a>, options: Options, initial_state: Option<(State<'a>, Span)>, initial_stack: Option<(Symbol<'a>, Span)>, + accept_by: Option<(AcceptBy, Span)>, states: HashMap, StateInfo>, states_def: Option, @@ -92,6 +99,18 @@ impl<'a> Pda<'a> { } } +macro_rules! accept_empty { + ($ident: ident) => { + $crate::maker!($ident: "N","n","null","empty","E","Z0","z0") + }; +} + +macro_rules! accept_final { + ($ident: ident) => { + $crate::maker!($ident: "F","final") + }; +} + impl<'a, 'b> PdaCompiler<'a, 'b> { pub fn new(ctx: &'b mut Context<'a>, options: Options) -> Self { Self { @@ -100,6 +119,7 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { initial_state: Default::default(), initial_stack: Default::default(), + accept_by: Default::default(), states: Default::default(), states_def: Default::default(), symbols: Default::default(), @@ -140,11 +160,32 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { .emit_info_logless(concat!("G can be ", gamma_upper!(str))); } - // if self.final_states_def.is_none() { - // self.ctx - // .emit_error_locless("final states never defined") - // .emit_help_logless("add: F = {...}"); - // } + if self.accept_by.is_none() { + self.ctx + .emit_error_locless("accept by never defined") + .emit_help_logless("add: accept = N|F") + .emit_info_logless(concat!( + "accept by empty stack N can be ", + accept_empty!(str) + )) + .emit_info_logless(concat!( + "accept by final state F can be ", + accept_final!(str) + )); + } + + if self.final_states_def.is_none() + && matches!(self.accept_by, Some((AcceptBy::FinalState, _))) + { + self.ctx + .emit_error_locless("final states never defined") + .emit_help_logless("add: F = {...}"); + }else if let (Some((AcceptBy::EmptyStack, empty)), Some(states)) = (self.accept_by, self.final_states_def){ + self.ctx + .emit_error_locless("final states defined alongside accept by empty stack") + .emit_help("either remote to accept by empty stack", states) + .emit_help("or remote to accept by final state", empty); + } let initial_state = match self.initial_state { Some(some) => some.0, @@ -194,13 +235,16 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { return None; } + let final_states = + matches!(self.accept_by, Some((AcceptBy::FinalState, _))).then_some(self.final_states); + Some(Pda { initial_state, initial_stack, states: self.states, symbols: self.symbols, alphabet: self.alphabet, - final_states: Some(self.final_states), + final_states, transitions: self.transitions, }) } @@ -209,6 +253,7 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { use Spanned as S; use ast::TopLevel as TL; match element { + TL::Item(S("accept", _), item) => self.compile_accept_by(item, span), TL::Item(S("Q", _), list) => self.compile_states(list, span), TL::Item(S(gamma_upper!(pat), _), list) => self.compile_symbols(list, span), TL::Item(S(sigma_upper!(pat), _), list) => self.compile_alphabet(list, span), @@ -216,7 +261,7 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { TL::Item(S(INITIAL_STATE, _), item) => self.compile_initial_state(item, span), TL::Item(S(INITIAL_STACK, _), item) => self.compile_initial_stack(item, span), TL::Item(S(name, dest_s), _) => { - self.ctx.emit_error(format!("unknown item {name:?}, expected states, stack symbols, alphabet, final states, initial state, initial stack"), dest_s); + self.ctx.emit_error(format!("unknown item {name:?}, expected states | stack symbols | alphabet | accept by | final states | initial state | initial stack"), dest_s); } TL::TransitionFunc(S((S(delta_lower!(pat), _), args), _), list) => { @@ -239,6 +284,28 @@ impl<'a, 'b> PdaCompiler<'a, 'b> { } } + fn compile_accept_by(&mut self, item: Spanned>, top_level: Span) { + if let Some((_, previous)) = self.accept_by { + self.ctx + .emit_error("accept by already set", top_level) + .emit_info("previously defined here", previous); + } + let Some(by) = item.expect_ident(self.ctx) else { + return; + }; + + let by = match by { + accept_empty!(pat) => AcceptBy::EmptyStack, + accept_final!(pat) => AcceptBy::FinalState, + _ => { + self.ctx.emit_error("invalid accept by", item.1); + return; + } + }; + + self.accept_by = Some((by, top_level)); + } + fn compile_states(&mut self, list: Spanned>, top_level: Span) { if let Some(previous) = self.states_def { self.ctx diff --git a/automata/src/automatan/tm.rs b/automata/src/automatan/tm.rs index 8e9906d..adc57f4 100644 --- a/automata/src/automatan/tm.rs +++ b/automata/src/automatan/tm.rs @@ -190,7 +190,7 @@ impl<'a, 'b> TmCompiler<'a, 'b> { TL::Item(S(INITIAL_STATE, _), item) => self.compile_initial_state(item, span), TL::Item(S(BLANK_SYMBOL, _), item) => self.compile_blank_symbol(item, span), TL::Item(S(name, dest_s), _) => { - self.ctx.emit_error(format!("unknown item {name:?}, expected states, symbols, final states, initial state, blank symbol"), dest_s); + self.ctx.emit_error(format!("unknown item {name:?}, expected states | symbols | final states | initial state | blank symbol"), dest_s); } TL::TransitionFunc(S((S(delta_lower!(pat), _), args), _), list) => { diff --git a/web/root/src/examples.ts b/web/root/src/examples.ts index 6745845..c3cd7e6 100644 --- a/web/root/src/examples.ts +++ b/web/root/src/examples.ts @@ -27,11 +27,11 @@ export const examples: readonly Example[] = [ "DFA", `// strings over a,b which start and end with different letters -type = DFA // type of machine DFA, NFA, DPDA, NPDA, DTM, NTM -Q = {q0, qa, qa', qb, qb'} // set of states -E = {a, b} // alphabet -F = {qa', qb'} // set of final states -q0 = q0 // initial state +type = DFA // type of machine DFA, NFA, DPDA, NPDA, DTM, NTM +Q = {q0, qa, qa', qb, qb'} // set of states +E = {a, b} // alphabet +F = {qa', qb'} // set of final states +q0 = q0 // initial state // transition function (state, letter) -> state d(q0, a) = qa @@ -81,11 +81,12 @@ d(q4, 3) = q2`, new Example( "DPDA", "unequal", - `type=DPDA -Q = {q0, qas, qeq, qmb, qlb} // states -E = {a, b} // alphabet -T = {z0, A} // stack -F = {qmb, qlb} // final states + `type = DPDA +Q = {q0, qas, qeq, qmb, qlb} // states +E = {a, b} // alphabet +T = {z0, A} // stack +F = {qmb, qlb} // final states +accept = F // accept by final state q0 = q0 z0 = z0 @@ -112,6 +113,7 @@ d(qmb, b, z0) = (qmb, z0)`, Q = {q0, q1} // states E = {a, b} // alphabet T = {z0, A, B} // stack +accept = E // accept by empty stack q0 = q0 z0 = z0 @@ -142,6 +144,7 @@ d(q1, b, B) = { (q1, epsilon) }`, Q = {q0, q1} // states E = {a, b} // alphabet T = {z0, A, B} // stack +accept = E // accept by empty stack q0 = q0 z0 = z0