mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
improved error messages for TM and PDA's
This commit is contained in:
parent
c06a0a0147
commit
d6e4fff782
12 changed files with 817 additions and 537 deletions
|
|
@ -3,11 +3,13 @@ use std::collections::HashSet;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
delta_lower, dual_struct_serde, epsilon, loader::{
|
delta_lower, dual_struct_serde, epsilon,
|
||||||
|
loader::{
|
||||||
Context, INITIAL_STATE, Spanned,
|
Context, INITIAL_STATE, Spanned,
|
||||||
ast::{self, Symbol as Sym, TopLevel},
|
ast::{self, Symbol as Sym, TopLevel},
|
||||||
log::LogSink,
|
log::LogSink,
|
||||||
}, sigma_upper
|
},
|
||||||
|
sigma_upper,
|
||||||
};
|
};
|
||||||
|
|
||||||
dual_struct_serde! {
|
dual_struct_serde! {
|
||||||
|
|
@ -104,6 +106,12 @@ impl<'a, 'b> FaCompiler<'a, 'b> {
|
||||||
self.compile_top_level(element, span);
|
self.compile_top_level(element, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.states_def.is_none() {
|
||||||
|
self.ctx
|
||||||
|
.emit_error_locless("states never defined")
|
||||||
|
.emit_help_logless("add: Q = {...}");
|
||||||
|
}
|
||||||
|
|
||||||
if self.alphabet_def.is_none() {
|
if self.alphabet_def.is_none() {
|
||||||
self.ctx
|
self.ctx
|
||||||
.emit_error_locless("alphabet never defined")
|
.emit_error_locless("alphabet never defined")
|
||||||
|
|
@ -111,12 +119,6 @@ impl<'a, 'b> FaCompiler<'a, 'b> {
|
||||||
.emit_info_logless(concat!("E can be ", sigma_upper!(str)));
|
.emit_info_logless(concat!("E can be ", sigma_upper!(str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.states_def.is_none() {
|
|
||||||
self.ctx
|
|
||||||
.emit_error_locless("states never defined")
|
|
||||||
.emit_help_logless("add: Q = {...}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.final_states_def.is_none() {
|
if self.final_states_def.is_none() {
|
||||||
self.ctx
|
self.ctx
|
||||||
.emit_error_locless("final states never defined")
|
.emit_error_locless("final states never defined")
|
||||||
|
|
@ -139,9 +141,12 @@ impl<'a, 'b> FaCompiler<'a, 'b> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.transitions.is_empty(){
|
if self.transitions.is_empty() {
|
||||||
self.ctx.emit_warning_locless("no transitions defined")
|
self.ctx
|
||||||
.emit_help_logless("consider defining one: d(state, letter|epsilon) = state | {state, state, ...}")
|
.emit_warning_locless("no transitions defined")
|
||||||
|
.emit_help_logless(
|
||||||
|
"consider defining one: d(state, letter|epsilon) = state | {state, ...}",
|
||||||
|
)
|
||||||
.emit_info_logless(concat!("d can be ", delta_lower!(str)))
|
.emit_info_logless(concat!("d can be ", delta_lower!(str)))
|
||||||
.emit_info_logless(concat!("epsilon can be ", epsilon!(str)));
|
.emit_info_logless(concat!("epsilon can be ", epsilon!(str)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ pub mod fa;
|
||||||
pub mod pda;
|
pub mod pda;
|
||||||
pub mod tm;
|
pub mod tm;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub non_deterministic: bool,
|
pub non_deterministic: bool,
|
||||||
|
|
@ -14,15 +13,27 @@ pub struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
serde(transparent)
|
||||||
|
)]
|
||||||
pub struct State<'a>(pub &'a str);
|
pub struct State<'a>(pub &'a str);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
serde(transparent)
|
||||||
|
)]
|
||||||
pub struct Symbol<'a>(pub &'a str);
|
pub struct Symbol<'a>(pub &'a str);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
serde(transparent)
|
||||||
|
)]
|
||||||
pub struct Letter<'a>(pub &'a str);
|
pub struct Letter<'a>(pub &'a str);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,15 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{delta_lower, dual_struct_serde, gamma_upper, loader::{
|
use crate::{
|
||||||
Context, INITIAL_STACK, INITIAL_STATE, Spanned, ast::{self, Symbol as Sym}, log::LogSink
|
delta_lower, dual_struct_serde, epsilon, gamma_upper,
|
||||||
}, sigma_upper};
|
loader::{
|
||||||
|
Context, INITIAL_STACK, INITIAL_STATE, Spanned,
|
||||||
|
ast::{self, Symbol as Sym},
|
||||||
|
log::LogSink,
|
||||||
|
},
|
||||||
|
sigma_upper,
|
||||||
|
};
|
||||||
|
|
||||||
dual_struct_serde! {
|
dual_struct_serde! {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
|
@ -54,315 +60,446 @@ dual_struct_serde! { {#[serde_with::serde_as]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PdaCompiler<'a, 'b> {
|
||||||
|
ctx: &'b mut Context<'a>,
|
||||||
|
options: Options,
|
||||||
|
|
||||||
|
initial_state: Option<(State<'a>, Span)>,
|
||||||
|
initial_stack: Option<(Symbol<'a>, Span)>,
|
||||||
|
|
||||||
|
states: HashMap<State<'a>, StateInfo>,
|
||||||
|
states_def: Option<Span>,
|
||||||
|
|
||||||
|
symbols: HashMap<Symbol<'a>, SymbolInfo>,
|
||||||
|
symbols_def: Option<Span>,
|
||||||
|
|
||||||
|
alphabet: HashMap<Letter<'a>, LetterInfo>,
|
||||||
|
alphabet_def: Option<Span>,
|
||||||
|
|
||||||
|
final_states: HashMap<State<'a>, StateInfo>,
|
||||||
|
final_states_def: Option<Span>,
|
||||||
|
|
||||||
|
transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Pda<'a> {
|
impl<'a> Pda<'a> {
|
||||||
pub fn compile(
|
pub fn compile(
|
||||||
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
ctx: &mut Context<'a>,
|
ctx: &mut Context<'a>,
|
||||||
options: Options,
|
options: Options,
|
||||||
) -> Option<Pda<'a>> {
|
) -> Option<Pda<'a>> {
|
||||||
let mut initial_state = None;
|
PdaCompiler::new(ctx, options).compile(items)
|
||||||
let mut initial_stack = None;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut states = HashMap::new();
|
impl<'a, 'b> PdaCompiler<'a, 'b> {
|
||||||
let mut symbols = HashMap::new();
|
pub fn new(ctx: &'b mut Context<'a>, options: Options) -> Self {
|
||||||
let mut alphabet = HashMap::new();
|
Self {
|
||||||
let mut final_states = None;
|
ctx,
|
||||||
|
options,
|
||||||
|
|
||||||
let mut transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>> =
|
initial_state: Default::default(),
|
||||||
HashMap::new();
|
initial_stack: Default::default(),
|
||||||
|
states: Default::default(),
|
||||||
|
states_def: Default::default(),
|
||||||
|
symbols: Default::default(),
|
||||||
|
symbols_def: Default::default(),
|
||||||
|
alphabet: Default::default(),
|
||||||
|
alphabet_def: Default::default(),
|
||||||
|
final_states: Default::default(),
|
||||||
|
final_states_def: Default::default(),
|
||||||
|
transitions: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(
|
||||||
|
mut self,
|
||||||
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
|
) -> Option<Pda<'a>> {
|
||||||
for Spanned(element, span) in items {
|
for Spanned(element, span) in items {
|
||||||
use Spanned as S;
|
self.compile_top_level(element, span);
|
||||||
use ast::TopLevel as TL;
|
|
||||||
match element {
|
|
||||||
TL::Item(S("Q", _), list) => {
|
|
||||||
if !states.is_empty() {
|
|
||||||
ctx.emit_error("states already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if states
|
|
||||||
.insert(State(ident), StateInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("state redefined", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if list.is_empty() {
|
|
||||||
ctx.emit_error("states cannot be empty", span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S(sigma_upper!(pat), _), list) => {
|
|
||||||
if !alphabet.is_empty() {
|
|
||||||
ctx.emit_error("alphabet already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ident.chars().count() != 1 {
|
|
||||||
ctx.emit_error("letter cannot be longer than one char", item.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if alphabet
|
|
||||||
.insert(Letter(ident), LetterInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("letter redefined", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if list.is_empty() {
|
|
||||||
ctx.emit_error("alphabet cannot be empty", span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S("F", _), list) => {
|
|
||||||
if final_states.is_some() {
|
|
||||||
ctx.emit_error("final states already set", span);
|
|
||||||
}
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if states.contains_key(&State(ident)) {
|
|
||||||
if map
|
|
||||||
.insert(State(ident), StateInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("final state redefined", item.1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.emit_error("final state not defined in set of states", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final_states = Some(map);
|
|
||||||
}
|
|
||||||
TL::Item(S(gamma_upper!(pat), _), list) => {
|
|
||||||
if !symbols.is_empty() {
|
|
||||||
ctx.emit_error("stack symbols already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if symbols
|
|
||||||
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("stack symbol redefined", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if list.is_empty() {
|
|
||||||
ctx.emit_error("stack symbols cannot be empty", span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S(INITIAL_STATE, _), S(src, src_d)) => match src {
|
|
||||||
ast::Item::Symbol(Sym::Ident(ident)) => {
|
|
||||||
if initial_state.is_some() {
|
|
||||||
ctx.emit_error("initial state already set", span);
|
|
||||||
}
|
|
||||||
if states.contains_key(&State(ident)) {
|
|
||||||
initial_state = Some(State(ident))
|
|
||||||
} else {
|
|
||||||
ctx.emit_error("initial state symbol not defined as a state", src_d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => _ = ctx.emit_error("expected ident", src_d),
|
|
||||||
},
|
|
||||||
TL::Item(S(INITIAL_STACK, _), S(src, src_d)) => match src {
|
|
||||||
ast::Item::Symbol(Sym::Ident(ident)) => {
|
|
||||||
if initial_stack.is_some() {
|
|
||||||
ctx.emit_error("initial stack already set", span);
|
|
||||||
}
|
|
||||||
if symbols.contains_key(&Symbol(ident)) {
|
|
||||||
initial_stack = Some(Symbol(ident));
|
|
||||||
} else {
|
|
||||||
ctx.emit_error(
|
|
||||||
"initial stack symbol not defined as a stack symbol",
|
|
||||||
src_d,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => _ = ctx.emit_error("expected ident", src_d),
|
|
||||||
},
|
|
||||||
TL::Item(S(name, dest_s), _) => {
|
|
||||||
ctx.emit_error(format!("unknown item {name:?}, expected states, alphabet, symbols, final states, initial state, initial stack"), dest_s);
|
|
||||||
}
|
|
||||||
|
|
||||||
TL::TransitionFunc(S((S(delta_lower!(pat), _), tuple), _), list) => {
|
|
||||||
let list = list.set_weak();
|
|
||||||
let Some((state, letter, stack_symbol)) =
|
|
||||||
tuple.as_ref().expect_pda_transition_function(ctx)
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if !states.contains_key(&State(state.0)) {
|
|
||||||
ctx.emit_error("transition state not defined as state", state.1);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if !symbols.contains_key(&Symbol(stack_symbol.0)) {
|
|
||||||
ctx.emit_error(
|
|
||||||
"transition stack symbol not defined as stack symbol",
|
|
||||||
stack_symbol.1,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let letter: Option<Letter<'_>> = match letter.0 {
|
|
||||||
Sym::Epsilon(_) => {
|
|
||||||
if !options.epsilon_moves {
|
|
||||||
ctx.emit_error("epsilon moves not permitted", letter.1);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Sym::Ident(val) => {
|
|
||||||
if !alphabet.contains_key(&Letter(val)) {
|
|
||||||
ctx.emit_error(
|
|
||||||
"transition letter not defined in alphabet",
|
|
||||||
letter.1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(Letter(val))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for item in list {
|
|
||||||
let Some((next_state, stack)) = item
|
|
||||||
.expect_tuple(ctx)
|
|
||||||
.and_then(|item| item.expect_pda_transition(ctx))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !states.contains_key(&State(next_state.0)) {
|
|
||||||
ctx.emit_error("transition state not defined as state", next_state.1);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let stack: Vec<_> = stack
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.filter_map(|symbol| {
|
|
||||||
if matches!(symbol.0, ast::Item::Symbol(Sym::Epsilon(_))) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let ident = symbol.expect_ident(ctx)?;
|
|
||||||
|
|
||||||
if !symbols.contains_key(&Symbol(ident)) {
|
|
||||||
ctx.emit_error("transition stack symbol not defined", symbol.1);
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(Symbol(ident))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let entry: &mut _ = transitions
|
|
||||||
.entry(TransitionFrom {
|
|
||||||
letter,
|
|
||||||
state: State(state.0),
|
|
||||||
symbol: Symbol(stack_symbol.0),
|
|
||||||
})
|
|
||||||
.or_default();
|
|
||||||
if !entry.is_empty() && !options.non_deterministic {
|
|
||||||
ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
|
||||||
}
|
|
||||||
if !entry.insert(TransitionTo {
|
|
||||||
state: State(next_state.0),
|
|
||||||
stack,
|
|
||||||
|
|
||||||
function: tuple.1,
|
|
||||||
transition: item.1,
|
|
||||||
}) {
|
|
||||||
ctx.emit_warning("duplicate transition", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
|
||||||
ctx.emit_error(
|
|
||||||
format!(
|
|
||||||
"unknown function {name:?}, expected transition function ( {} )", delta_lower!(str)
|
|
||||||
),
|
|
||||||
dest_s,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TL::ProductionRule(_, _) => {
|
|
||||||
ctx.emit_error("unexpected production rule", span);
|
|
||||||
}
|
|
||||||
TL::Table() => _ = ctx.emit_error("unexpected table", span),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if symbols.is_empty() {
|
if self.states_def.is_none() {
|
||||||
ctx.emit_error_locless("stack symbols never defined");
|
self.ctx
|
||||||
|
.emit_error_locless("states never defined")
|
||||||
|
.emit_help_logless("add: Q = {...}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if alphabet.is_empty() {
|
if self.alphabet_def.is_none() {
|
||||||
ctx.emit_error_locless("alphabet never defined");
|
self.ctx
|
||||||
|
.emit_error_locless("alphabet never defined")
|
||||||
|
.emit_help_logless("add: E = {...}")
|
||||||
|
.emit_info_logless(concat!("E can be ", sigma_upper!(str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if states.is_empty() {
|
if self.symbols_def.is_none() {
|
||||||
ctx.emit_error_locless("states never defined");
|
self.ctx
|
||||||
|
.emit_error_locless("stack symbols never defined")
|
||||||
|
.emit_help_logless("add: G = {...}")
|
||||||
|
.emit_info_logless(concat!("G can be ", gamma_upper!(str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let initial_stack = match initial_stack {
|
// if self.final_states_def.is_none() {
|
||||||
Some(some) => some,
|
// self.ctx
|
||||||
|
// .emit_error_locless("final states never defined")
|
||||||
|
// .emit_help_logless("add: F = {...}");
|
||||||
|
// }
|
||||||
|
|
||||||
|
let initial_state = match self.initial_state {
|
||||||
|
Some(some) => some.0,
|
||||||
None => {
|
None => {
|
||||||
if symbols.contains_key(&Symbol("Z0")) {
|
if self.states.contains_key(&State("q0")) {
|
||||||
ctx.emit_warning_locless(
|
self.ctx
|
||||||
"initial stack symbol not defined, defaulting to 'Z0'",
|
.emit_warning_locless("initial state not defined, defaulting to 'q0'")
|
||||||
);
|
.emit_help_logless(format!("add: {INITIAL_STATE} = q0"));
|
||||||
} else {
|
} else {
|
||||||
ctx.emit_error_locless("initial stack symbol not defined");
|
self.ctx
|
||||||
}
|
.emit_error_locless("initial state not defined")
|
||||||
Symbol("Z0")
|
.emit_help_logless(format!("add: {INITIAL_STATE} = ..."));
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let initial_state = match initial_state {
|
|
||||||
Some(some) => some,
|
|
||||||
None => {
|
|
||||||
if states.contains_key(&State("q0")) {
|
|
||||||
ctx.emit_warning_locless("initial state not defined, defaulting to 'q0'");
|
|
||||||
} else {
|
|
||||||
ctx.emit_error_locless("initial state not defined");
|
|
||||||
}
|
}
|
||||||
State("q0")
|
State("q0")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ctx.contains_errors() {
|
let initial_stack = match self.initial_stack {
|
||||||
|
Some(some) => some.0,
|
||||||
|
None => {
|
||||||
|
if self.symbols.contains_key(&Symbol("Z0")) {
|
||||||
|
self.ctx
|
||||||
|
.emit_warning_locless(
|
||||||
|
"initial stack symbol not defined, defaulting to 'Z0'",
|
||||||
|
)
|
||||||
|
.emit_help_logless(format!("add: {INITIAL_STACK} = Z0"));
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error_locless("initial stack symbol not defined")
|
||||||
|
.emit_help_logless(format!("add: {INITIAL_STACK} = ..."));
|
||||||
|
}
|
||||||
|
Symbol("Z0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.transitions.is_empty() {
|
||||||
|
self.ctx
|
||||||
|
.emit_warning_locless("no transitions defined")
|
||||||
|
.emit_help_logless(
|
||||||
|
"consider defining one: d(state, letter|epsilon, symbol) = (state, [symbol]) | {(state, [symbol]), ...}",
|
||||||
|
)
|
||||||
|
.emit_info_logless(concat!("d can be ", delta_lower!(str)))
|
||||||
|
.emit_info_logless(concat!("epsilon can be ", epsilon!(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ctx.contains_errors() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Pda {
|
Some(Pda {
|
||||||
initial_state,
|
initial_state,
|
||||||
initial_stack,
|
initial_stack,
|
||||||
states,
|
states: self.states,
|
||||||
symbols,
|
symbols: self.symbols,
|
||||||
alphabet,
|
alphabet: self.alphabet,
|
||||||
final_states,
|
final_states: Some(self.final_states),
|
||||||
transitions,
|
transitions: self.transitions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_top_level(&mut self, element: ast::TopLevel<'a>, span: Span) {
|
||||||
|
use Spanned as S;
|
||||||
|
use ast::TopLevel as TL;
|
||||||
|
match element {
|
||||||
|
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),
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::TransitionFunc(S((S(delta_lower!(pat), _), args), _), list) => {
|
||||||
|
self.compile_transition_function(args, list)
|
||||||
|
}
|
||||||
|
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
||||||
|
self.ctx.emit_error(
|
||||||
|
format!(
|
||||||
|
"unknown function {name:?}, expected transition function ( {} )",
|
||||||
|
delta_lower!(str)
|
||||||
|
),
|
||||||
|
dest_s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::ProductionRule(_, _) => {
|
||||||
|
self.ctx.emit_error("unexpected production rule", span);
|
||||||
|
}
|
||||||
|
TL::Table() => _ = self.ctx.emit_error("unexpected table", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_states(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.states_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("states already set", top_level)
|
||||||
|
.emit_info("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(previous) = self
|
||||||
|
.states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
{
|
||||||
|
self.ctx
|
||||||
|
.emit_error("state redefined", item.1)
|
||||||
|
.emit_info("previously defined here", previous.definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
self.ctx.emit_error("states cannot be empty", top_level);
|
||||||
|
}
|
||||||
|
self.states_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_symbols(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.symbols_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("stack symbols already set", top_level)
|
||||||
|
.emit_info("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(previous) = self
|
||||||
|
.symbols
|
||||||
|
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
||||||
|
{
|
||||||
|
self.ctx
|
||||||
|
.emit_error("stack symbol redefined", item.1)
|
||||||
|
.emit_info("previously defined here", previous.definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
self.ctx.emit_error("states cannot be empty", top_level);
|
||||||
|
}
|
||||||
|
self.symbols_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_alphabet(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.alphabet_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("alphabet already set", top_level)
|
||||||
|
.emit_info("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ident.chars().count() != 1 {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("letter cannot be longer than one char", item.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(previous) = self
|
||||||
|
.alphabet
|
||||||
|
.insert(Letter(ident), LetterInfo { definition: item.1 })
|
||||||
|
{
|
||||||
|
self.ctx
|
||||||
|
.emit_error("letter redefined", item.1)
|
||||||
|
.emit_help("previously defined here", previous.definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if list.is_empty() {
|
||||||
|
self.ctx.emit_error("alphabet cannot be empty", top_level);
|
||||||
|
}
|
||||||
|
self.alphabet_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_final_states(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.final_states_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("final states already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if self.states.contains_key(&State(ident)) {
|
||||||
|
if self
|
||||||
|
.final_states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.ctx.emit_error("final state redefined", item.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("final state not defined in set of states", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.final_states_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_initial_state(
|
||||||
|
&mut self,
|
||||||
|
Spanned(src, src_d): Spanned<ast::Item<'a>>,
|
||||||
|
top_level: Span,
|
||||||
|
) {
|
||||||
|
match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if let Some((_, previous)) = self.initial_state {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial state already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
if self.states.contains_key(&State(ident)) {
|
||||||
|
self.initial_state = Some((State(ident), top_level))
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial state symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => _ = self.ctx.emit_error("expected ident", src_d),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_initial_stack(
|
||||||
|
&mut self,
|
||||||
|
Spanned(src, src_d): Spanned<ast::Item<'a>>,
|
||||||
|
top_level: Span,
|
||||||
|
) {
|
||||||
|
match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if let Some((_, previous)) = self.initial_stack {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial stack symbol already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
if self.symbols.contains_key(&Symbol(ident)) {
|
||||||
|
self.initial_stack = Some((Symbol(ident), top_level))
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial stack symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => _ = self.ctx.emit_error("expected ident", src_d),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_transition_function(
|
||||||
|
&mut self,
|
||||||
|
args: Spanned<ast::Tuple<'a>>,
|
||||||
|
list: Spanned<ast::Item<'a>>,
|
||||||
|
) {
|
||||||
|
let list = list.set_weak();
|
||||||
|
let Some((state, letter, stack_symbol)) =
|
||||||
|
args.as_ref().expect_pda_transition_function(self.ctx)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !self.states.contains_key(&State(state.0)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition state not defined as state", state.1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !self.symbols.contains_key(&Symbol(stack_symbol.0)) {
|
||||||
|
self.ctx.emit_error(
|
||||||
|
"transition stack symbol not defined as stack symbol",
|
||||||
|
stack_symbol.1,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let letter: Option<Letter<'_>> = match letter.0 {
|
||||||
|
Sym::Epsilon(_) => {
|
||||||
|
if !self.options.epsilon_moves {
|
||||||
|
self.ctx.emit_error("epsilon moves not permitted", letter.1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Sym::Ident(val) => {
|
||||||
|
if !self.alphabet.contains_key(&Letter(val)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition letter not defined in alphabet", letter.1);
|
||||||
|
}
|
||||||
|
Some(Letter(val))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
let Some((next_state, stack)) = item
|
||||||
|
.expect_tuple(self.ctx)
|
||||||
|
.and_then(|item| item.expect_pda_transition(self.ctx))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.states.contains_key(&State(next_state.0)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition state not defined as state", next_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let stack: Vec<_> = stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter_map(|symbol| {
|
||||||
|
if matches!(symbol.0, ast::Item::Symbol(Sym::Epsilon(_))) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ident = symbol.expect_ident(self.ctx)?;
|
||||||
|
|
||||||
|
if !self.symbols.contains_key(&Symbol(ident)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition stack symbol not defined", symbol.1);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(Symbol(ident))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let entry: &mut _ = self
|
||||||
|
.transitions
|
||||||
|
.entry(TransitionFrom {
|
||||||
|
letter,
|
||||||
|
state: State(state.0),
|
||||||
|
symbol: Symbol(stack_symbol.0),
|
||||||
|
})
|
||||||
|
.or_default();
|
||||||
|
if !entry.is_empty() && !self.options.non_deterministic {
|
||||||
|
self.ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
||||||
|
}
|
||||||
|
if !entry.insert(TransitionTo {
|
||||||
|
state: State(next_state.0),
|
||||||
|
stack,
|
||||||
|
|
||||||
|
function: args.1,
|
||||||
|
transition: item.1,
|
||||||
|
}) {
|
||||||
|
self.ctx.emit_warning("duplicate transition", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Spanned<&'b ast::Tuple<'a>> {
|
impl<'a, 'b> Spanned<&'b ast::Tuple<'a>> {
|
||||||
|
|
@ -382,10 +519,12 @@ impl<'a, 'b> Spanned<&'b ast::Tuple<'a>> {
|
||||||
Spanned(symbol, *symbol_span),
|
Spanned(symbol, *symbol_span),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ => _ = ctx.emit_error(
|
_ => {
|
||||||
"expected PDA transition function (state, letter|epsilon, symbol)",
|
_ = ctx.emit_error(
|
||||||
self.1,
|
"expected PDA transition function (state, letter|epsilon, symbol)",
|
||||||
),
|
self.1,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,14 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{delta_lower, dual_struct_serde, gamma_upper, loader::{
|
use crate::{
|
||||||
BLANK_SYMBOL, Context, Spanned, ast::{self, Symbol as Sym}, log::LogSink
|
delta_lower, dual_struct_serde,
|
||||||
}};
|
loader::{
|
||||||
|
BLANK_SYMBOL, Context, INITIAL_STATE, Spanned,
|
||||||
|
ast::{self, Symbol as Sym},
|
||||||
|
log::LogSink,
|
||||||
|
},
|
||||||
|
};
|
||||||
dual_struct_serde! {
|
dual_struct_serde! {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
pub struct TransitionFrom<'a> {
|
pub struct TransitionFrom<'a> {
|
||||||
|
|
@ -43,7 +48,7 @@ dual_struct_serde! {{#[serde_with::serde_as]}
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub initial_state: State<'a>,
|
pub initial_state: State<'a>,
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub initial_tape: Symbol<'a>,
|
pub blank_symbol: Symbol<'a>,
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub states: HashMap<State<'a>, StateInfo>,
|
pub states: HashMap<State<'a>, StateInfo>,
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
|
|
@ -52,7 +57,7 @@ dual_struct_serde! {{#[serde_with::serde_as]}
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub final_states: HashMap<State<'a>, StateInfo>,
|
pub final_states: HashMap<State<'a>, StateInfo>,
|
||||||
|
|
||||||
|
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
#[serde_as(as = "serde_with::Seq<(_, _)>")]
|
#[serde_as(as = "serde_with::Seq<(_, _)>")]
|
||||||
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
|
@ -65,237 +70,341 @@ impl<'a> Tm<'a> {
|
||||||
ctx: &mut Context<'a>,
|
ctx: &mut Context<'a>,
|
||||||
options: Options,
|
options: Options,
|
||||||
) -> Option<Tm<'a>> {
|
) -> Option<Tm<'a>> {
|
||||||
let mut initial_state = None;
|
TmCompiler::new(ctx, options).compile(items)
|
||||||
let mut initial_tape = None;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut states = HashMap::new();
|
pub struct TmCompiler<'a, 'b> {
|
||||||
let mut symbols = HashMap::new();
|
ctx: &'b mut Context<'a>,
|
||||||
let mut final_states = HashMap::new();
|
options: Options,
|
||||||
|
|
||||||
let mut transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>> =
|
initial_state: Option<(State<'a>, Span)>,
|
||||||
HashMap::new();
|
blank_symbol: Option<(Symbol<'a>, Span)>,
|
||||||
|
|
||||||
|
states: HashMap<State<'a>, StateInfo>,
|
||||||
|
states_def: Option<Span>,
|
||||||
|
|
||||||
|
symbols: HashMap<Symbol<'a>, SymbolInfo>,
|
||||||
|
symbols_def: Option<Span>,
|
||||||
|
|
||||||
|
final_states: HashMap<State<'a>, StateInfo>,
|
||||||
|
final_states_def: Option<Span>,
|
||||||
|
|
||||||
|
transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> TmCompiler<'a, 'b> {
|
||||||
|
pub fn new(ctx: &'b mut Context<'a>, options: Options) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
options,
|
||||||
|
|
||||||
|
initial_state: Default::default(),
|
||||||
|
blank_symbol: Default::default(),
|
||||||
|
states: Default::default(),
|
||||||
|
states_def: Default::default(),
|
||||||
|
symbols: Default::default(),
|
||||||
|
symbols_def: Default::default(),
|
||||||
|
final_states: Default::default(),
|
||||||
|
final_states_def: Default::default(),
|
||||||
|
transitions: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(
|
||||||
|
mut self,
|
||||||
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
|
) -> Option<Tm<'a>> {
|
||||||
for Spanned(element, span) in items {
|
for Spanned(element, span) in items {
|
||||||
use Spanned as S;
|
self.compile_top_level(element, span);
|
||||||
use ast::TopLevel as TL;
|
|
||||||
match element {
|
|
||||||
TL::Item(S("Q", _), list) => {
|
|
||||||
if !states.is_empty() {
|
|
||||||
ctx.emit_error("states already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if states
|
|
||||||
.insert(State(ident), StateInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("state redefined", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if list.is_empty() {
|
|
||||||
ctx.emit_error("states cannot be empty", span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S("F", _), list) => {
|
|
||||||
if !final_states.is_empty() {
|
|
||||||
ctx.emit_error("final states already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if states.contains_key(&State(ident)) {
|
|
||||||
if final_states
|
|
||||||
.insert(State(ident), StateInfo { definition: item.1 })
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
ctx.emit_error("final state redefined", item.1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.emit_error("final state not defined in set of states", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S(gamma_upper!(pat), _), list) => {
|
|
||||||
if !symbols.is_empty() {
|
|
||||||
ctx.emit_error("tape symbols already set", span);
|
|
||||||
}
|
|
||||||
let Some(list) = list.expect_set(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
for item in list {
|
|
||||||
let Some(ident) = item.expect_ident(ctx) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if symbols
|
|
||||||
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.emit_error("tape symbol redefined", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if list.is_empty() {
|
|
||||||
ctx.emit_error("tape symbols cannot be empty", span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::Item(S("q0", _), S(src, src_d)) => match src {
|
|
||||||
ast::Item::Symbol(Sym::Ident(ident)) => {
|
|
||||||
if initial_state.is_some() {
|
|
||||||
ctx.emit_error("initial state already set", span);
|
|
||||||
}
|
|
||||||
if states.contains_key(&State(ident)) {
|
|
||||||
initial_state = Some(State(ident))
|
|
||||||
} else {
|
|
||||||
ctx.emit_error("initial state symbol not defined as a state", src_d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => _ = ctx.emit_error("expected ident", src_d),
|
|
||||||
},
|
|
||||||
TL::Item(S(BLANK_SYMBOL, _), S(src, src_d)) => match src {
|
|
||||||
ast::Item::Symbol(Sym::Ident(ident)) => {
|
|
||||||
if initial_tape.is_some() {
|
|
||||||
ctx.emit_error("initial tape symbol already set", span);
|
|
||||||
}
|
|
||||||
if symbols.contains_key(&Symbol(ident)) {
|
|
||||||
initial_tape = Some(Symbol(ident));
|
|
||||||
} else {
|
|
||||||
ctx.emit_error(
|
|
||||||
"initial tape symbol not defined as a tape symbol",
|
|
||||||
src_d,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => _ = ctx.emit_error("expected ident", src_d),
|
|
||||||
},
|
|
||||||
TL::Item(S(name, dest_s), _) => {
|
|
||||||
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), _), tuple), _), list) => {
|
|
||||||
let list = list.set_weak();
|
|
||||||
let Some((from_state, from_tape)) =
|
|
||||||
tuple.as_ref().expect_tm_transition_function(ctx)
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if !states.contains_key(&State(from_state.0)) {
|
|
||||||
ctx.emit_error("transition state not defined as state", from_state.1);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if !symbols.contains_key(&Symbol(from_tape.0)) {
|
|
||||||
ctx.emit_error(
|
|
||||||
"transition tape symbol not defined as tape symbol",
|
|
||||||
from_tape.1,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
for item in list {
|
|
||||||
let Some((to_state, to_tape, direction)) = item
|
|
||||||
.expect_tuple(ctx)
|
|
||||||
.and_then(|item| item.expect_tm_transition(ctx))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !states.contains_key(&State(to_state.0)) {
|
|
||||||
ctx.emit_error("transition state not defined as state", to_state.1);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let entry: &mut _ = transitions
|
|
||||||
.entry(TransitionFrom {
|
|
||||||
state: State(from_state.0),
|
|
||||||
symbol: Symbol(from_tape.0),
|
|
||||||
})
|
|
||||||
.or_default();
|
|
||||||
if !entry.is_empty() && !options.non_deterministic {
|
|
||||||
ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
|
||||||
}
|
|
||||||
if !entry.insert(TransitionTo {
|
|
||||||
state: State(to_state.0),
|
|
||||||
symbol: Symbol(to_tape.0),
|
|
||||||
direction: direction.0,
|
|
||||||
|
|
||||||
function: tuple.1,
|
|
||||||
transition: item.1,
|
|
||||||
}) {
|
|
||||||
ctx.emit_warning("duplicate transition", item.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
|
||||||
ctx.emit_error(
|
|
||||||
format!(
|
|
||||||
"unknown function {name:?}, expected transition function ( {} )", delta_lower!(str)
|
|
||||||
),
|
|
||||||
dest_s,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TL::ProductionRule(_, _) => {
|
|
||||||
ctx.emit_error("unexpected production rule", span);
|
|
||||||
}
|
|
||||||
TL::Table() => _ = ctx.emit_error("unexpected table", span),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if symbols.is_empty() {
|
if self.final_states_def.is_none() {
|
||||||
ctx.emit_error_locless("tape symbols never defined");
|
self.ctx
|
||||||
|
.emit_error_locless("final states never defined")
|
||||||
|
.emit_help_logless("add: F = {...}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if states.is_empty() {
|
let initial_state = match self.initial_state {
|
||||||
ctx.emit_error_locless("states never defined");
|
Some(some) => some.0,
|
||||||
}
|
|
||||||
|
|
||||||
let initial_tape = match initial_tape {
|
|
||||||
Some(some) => some,
|
|
||||||
None => {
|
None => {
|
||||||
if symbols.contains_key(&Symbol("z0")) {
|
if self.states.contains_key(&State("q0")) {
|
||||||
ctx.emit_warning_locless("initial tape symbol not defined, defaulting to 'z0'");
|
self.ctx
|
||||||
|
.emit_warning_locless("initial state not defined, defaulting to 'q0'")
|
||||||
|
.emit_help_logless(format!("add: {INITIAL_STATE} = q0"));
|
||||||
} else {
|
} else {
|
||||||
ctx.emit_error_locless("initial tape symbol not defined");
|
self.ctx
|
||||||
}
|
.emit_error_locless("initial state not defined")
|
||||||
Symbol("z0")
|
.emit_help_logless(format!("add: {BLANK_SYMBOL} = ..."));
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let initial_state = match initial_state {
|
|
||||||
Some(some) => some,
|
|
||||||
None => {
|
|
||||||
if states.contains_key(&State("q0")) {
|
|
||||||
ctx.emit_warning_locless("initial state not defined, defaulting to 'q0'");
|
|
||||||
} else {
|
|
||||||
ctx.emit_error_locless("initial state not defined");
|
|
||||||
}
|
}
|
||||||
State("q0")
|
State("q0")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ctx.contains_errors() {
|
let blank_symbol = match self.blank_symbol {
|
||||||
|
Some(some) => some.0,
|
||||||
|
None => {
|
||||||
|
if self.symbols.contains_key(&Symbol("B")) {
|
||||||
|
self.ctx
|
||||||
|
.emit_warning_locless("blank symbol not defined, defaulting to 'B'")
|
||||||
|
.emit_help_logless(format!("add: {BLANK_SYMBOL} = B"));
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error_locless("blank symbol not defined")
|
||||||
|
.emit_help_logless(format!("add: {BLANK_SYMBOL} = ..."));
|
||||||
|
}
|
||||||
|
Symbol("B")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.transitions.is_empty() {
|
||||||
|
self.ctx
|
||||||
|
.emit_warning_locless("no transitions defined")
|
||||||
|
.emit_help_logless(
|
||||||
|
"consider defining one: d(state, symbol) = (state, symbol, direction) | {(state, symbol, direction), ...}",
|
||||||
|
)
|
||||||
|
.emit_info_logless(concat!("d can be ", delta_lower!(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ctx.contains_errors() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Tm {
|
Some(Tm {
|
||||||
initial_state,
|
initial_state,
|
||||||
initial_tape,
|
blank_symbol,
|
||||||
states,
|
states: self.states,
|
||||||
symbols,
|
symbols: self.symbols,
|
||||||
final_states,
|
final_states: self.final_states,
|
||||||
transitions,
|
transitions: self.transitions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_top_level(&mut self, element: ast::TopLevel<'a>, span: Span) {
|
||||||
|
use Spanned as S;
|
||||||
|
use ast::TopLevel as TL;
|
||||||
|
match element {
|
||||||
|
TL::Item(S("Q", _), list) => self.compile_states(list, span),
|
||||||
|
TL::Item(S(delta_lower!(pat), _), list) => self.compile_symbols(list, span),
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::TransitionFunc(S((S(delta_lower!(pat), _), args), _), list) => {
|
||||||
|
self.compile_transition_function(args, list)
|
||||||
|
}
|
||||||
|
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
||||||
|
self.ctx.emit_error(
|
||||||
|
format!(
|
||||||
|
"unknown function {name:?}, expected transition function ( {} )",
|
||||||
|
delta_lower!(str)
|
||||||
|
),
|
||||||
|
dest_s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::ProductionRule(_, _) => {
|
||||||
|
self.ctx.emit_error("unexpected production rule", span);
|
||||||
|
}
|
||||||
|
TL::Table() => _ = self.ctx.emit_error("unexpected table", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_states(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.states_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("states already set", top_level)
|
||||||
|
.emit_info("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(previous) = self
|
||||||
|
.states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
{
|
||||||
|
self.ctx
|
||||||
|
.emit_error("state redefined", item.1)
|
||||||
|
.emit_info("previously defined here", previous.definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
self.ctx.emit_error("states cannot be empty", top_level);
|
||||||
|
}
|
||||||
|
self.states_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_symbols(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.symbols_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("stack symbols already set", top_level)
|
||||||
|
.emit_info("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(previous) = self
|
||||||
|
.symbols
|
||||||
|
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
||||||
|
{
|
||||||
|
self.ctx
|
||||||
|
.emit_error("stack symbol redefined", item.1)
|
||||||
|
.emit_info("previously defined here", previous.definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
self.ctx.emit_error("states cannot be empty", top_level);
|
||||||
|
}
|
||||||
|
self.symbols_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_final_states(&mut self, list: Spanned<ast::Item<'a>>, top_level: Span) {
|
||||||
|
if let Some(previous) = self.final_states_def {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("final states already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(self.ctx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(self.ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if self.states.contains_key(&State(ident)) {
|
||||||
|
if self
|
||||||
|
.final_states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.ctx.emit_error("final state redefined", item.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("final state not defined in set of states", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.final_states_def = Some(top_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_initial_state(
|
||||||
|
&mut self,
|
||||||
|
Spanned(src, src_d): Spanned<ast::Item<'a>>,
|
||||||
|
top_level: Span,
|
||||||
|
) {
|
||||||
|
match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if let Some((_, previous)) = self.initial_state {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial state already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
if self.states.contains_key(&State(ident)) {
|
||||||
|
self.initial_state = Some((State(ident), top_level))
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("initial state symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => _ = self.ctx.emit_error("expected ident", src_d),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_blank_symbol(
|
||||||
|
&mut self,
|
||||||
|
Spanned(src, src_d): Spanned<ast::Item<'a>>,
|
||||||
|
top_level: Span,
|
||||||
|
) {
|
||||||
|
match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if let Some((_, previous)) = self.blank_symbol {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("blank symbol already set", top_level)
|
||||||
|
.emit_help("previously defined here", previous);
|
||||||
|
}
|
||||||
|
if self.states.contains_key(&State(ident)) {
|
||||||
|
self.blank_symbol = Some((Symbol(ident), top_level))
|
||||||
|
} else {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("blank symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => _ = self.ctx.emit_error("expected ident", src_d),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_transition_function(
|
||||||
|
&mut self,
|
||||||
|
args: Spanned<ast::Tuple<'a>>,
|
||||||
|
list: Spanned<ast::Item<'a>>,
|
||||||
|
) {
|
||||||
|
let list = list.set_weak();
|
||||||
|
let Some((from_state, from_tape)) = args.as_ref().expect_tm_transition_function(self.ctx)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !self.states.contains_key(&State(from_state.0)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition state not defined as state", from_state.1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !self.symbols.contains_key(&Symbol(from_tape.0)) {
|
||||||
|
self.ctx.emit_error(
|
||||||
|
"transition tape symbol not defined as tape symbol",
|
||||||
|
from_tape.1,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
let Some((to_state, to_tape, direction)) = item
|
||||||
|
.expect_tuple(self.ctx)
|
||||||
|
.and_then(|item| item.expect_tm_transition(self.ctx))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.states.contains_key(&State(to_state.0)) {
|
||||||
|
self.ctx
|
||||||
|
.emit_error("transition state not defined as state", to_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry: &mut _ = self
|
||||||
|
.transitions
|
||||||
|
.entry(TransitionFrom {
|
||||||
|
state: State(from_state.0),
|
||||||
|
symbol: Symbol(from_tape.0),
|
||||||
|
})
|
||||||
|
.or_default();
|
||||||
|
if !entry.is_empty() && !self.options.non_deterministic {
|
||||||
|
self.ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
||||||
|
}
|
||||||
|
if !entry.insert(TransitionTo {
|
||||||
|
state: State(to_state.0),
|
||||||
|
symbol: Symbol(to_tape.0),
|
||||||
|
direction: direction.0,
|
||||||
|
|
||||||
|
function: args.1,
|
||||||
|
transition: item.1,
|
||||||
|
}) {
|
||||||
|
self.ctx.emit_warning("duplicate transition", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Spanned<&ast::Tuple<'a>> {
|
impl<'a> Spanned<&ast::Tuple<'a>> {
|
||||||
|
|
@ -343,10 +452,12 @@ impl<'a> Spanned<&ast::Tuple<'a>> {
|
||||||
Spanned(direction, *direction_span),
|
Spanned(direction, *direction_span),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ => _ = ctx.emit_error(
|
_ => {
|
||||||
"expected TM transition function (state, symbol, direction)",
|
_ = ctx.emit_error(
|
||||||
self.1,
|
"expected TM transition function (state, symbol, direction)",
|
||||||
),
|
self.1,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,9 @@ impl<'a> Spanned<Item<'a>> {
|
||||||
pub fn expect_ident(&self, ctx: &mut Context<'a>) -> Option<&'a str> {
|
pub fn expect_ident(&self, ctx: &mut Context<'a>) -> Option<&'a str> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(ident)) => return Some(ident),
|
Item::Symbol(Symbol::Ident(ident)) => return Some(ident),
|
||||||
Item::Symbol(Symbol::Epsilon(_)) => _ = ctx.emit_error("expected ident found epsilon", self.1),
|
Item::Symbol(Symbol::Epsilon(_)) => {
|
||||||
|
_ = ctx.emit_error("expected ident found epsilon", self.1)
|
||||||
|
}
|
||||||
Item::Tuple(_) => _ = ctx.emit_error("expected ident found tuple", self.1),
|
Item::Tuple(_) => _ = ctx.emit_error("expected ident found tuple", self.1),
|
||||||
Item::List(_) => _ = ctx.emit_error("expected ident found list", self.1),
|
Item::List(_) => _ = ctx.emit_error("expected ident found list", self.1),
|
||||||
}
|
}
|
||||||
|
|
@ -86,8 +88,12 @@ impl<'a> Spanned<Item<'a>> {
|
||||||
|
|
||||||
pub fn expect_set(&self, ctx: &mut Context<'a>) -> Option<&[Spanned<Item<'a>>]> {
|
pub fn expect_set(&self, ctx: &mut Context<'a>) -> Option<&[Spanned<Item<'a>>]> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(_)) => _ = ctx.emit_error("expected set found ident", self.1),
|
Item::Symbol(Symbol::Ident(_)) => {
|
||||||
Item::Symbol(Symbol::Epsilon(_)) => _ = ctx.emit_error("expected set found epsilon", self.1),
|
_ = ctx.emit_error("expected set found ident", self.1)
|
||||||
|
}
|
||||||
|
Item::Symbol(Symbol::Epsilon(_)) => {
|
||||||
|
_ = ctx.emit_error("expected set found epsilon", self.1)
|
||||||
|
}
|
||||||
Item::Tuple(_) => _ = ctx.emit_error("expected set found tuple", self.1),
|
Item::Tuple(_) => _ = ctx.emit_error("expected set found tuple", self.1),
|
||||||
Item::List(list) => return Some(&list.0),
|
Item::List(list) => return Some(&list.0),
|
||||||
}
|
}
|
||||||
|
|
@ -96,8 +102,12 @@ impl<'a> Spanned<Item<'a>> {
|
||||||
|
|
||||||
pub fn expect_list(&self, ctx: &mut Context<'a>) -> Option<&[Spanned<Item<'a>>]> {
|
pub fn expect_list(&self, ctx: &mut Context<'a>) -> Option<&[Spanned<Item<'a>>]> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(_)) => _ = ctx.emit_error("expected list found ident", self.1),
|
Item::Symbol(Symbol::Ident(_)) => {
|
||||||
Item::Symbol(Symbol::Epsilon(_)) => _ = ctx.emit_error("expected list found epsilon", self.1),
|
_ = ctx.emit_error("expected list found ident", self.1)
|
||||||
|
}
|
||||||
|
Item::Symbol(Symbol::Epsilon(_)) => {
|
||||||
|
_ = ctx.emit_error("expected list found epsilon", self.1)
|
||||||
|
}
|
||||||
Item::Tuple(_) => _ = ctx.emit_error("expected list found tuple", self.1),
|
Item::Tuple(_) => _ = ctx.emit_error("expected list found tuple", self.1),
|
||||||
Item::List(list) => return Some(&list.0),
|
Item::List(list) => return Some(&list.0),
|
||||||
}
|
}
|
||||||
|
|
@ -120,8 +130,12 @@ impl<'a> Spanned<Item<'a>> {
|
||||||
|
|
||||||
pub fn expect_tuple(&self, ctx: &mut Context<'a>) -> Option<Spanned<&Tuple<'a>>> {
|
pub fn expect_tuple(&self, ctx: &mut Context<'a>) -> Option<Spanned<&Tuple<'a>>> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(_)) => _ = ctx.emit_error("expected tuple found ident", self.1),
|
Item::Symbol(Symbol::Ident(_)) => {
|
||||||
Item::Symbol(Symbol::Epsilon(_)) => _ = ctx.emit_error("expected tuple found epsilon", self.1),
|
_ = ctx.emit_error("expected tuple found ident", self.1)
|
||||||
|
}
|
||||||
|
Item::Symbol(Symbol::Epsilon(_)) => {
|
||||||
|
_ = ctx.emit_error("expected tuple found epsilon", self.1)
|
||||||
|
}
|
||||||
Item::Tuple(tuple) => return Some(Spanned(tuple, self.1)),
|
Item::Tuple(tuple) => return Some(Spanned(tuple, self.1)),
|
||||||
Item::List(_) => _ = ctx.emit_error("expected tuple found list", self.1),
|
Item::List(_) => _ = ctx.emit_error("expected tuple found list", self.1),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,10 @@ fn begin_ident(c: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn continue_ident(c: char) -> bool {
|
fn continue_ident(c: char) -> bool {
|
||||||
c.is_alphanumeric() || c == '_' || c=='\'' || (!c.is_ascii() && !c.is_control() && !c.is_whitespace())
|
c.is_alphanumeric()
|
||||||
|
|| c == '_'
|
||||||
|
|| c == '\''
|
||||||
|
|| (!c.is_ascii() && !c.is_control() && !c.is_whitespace())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::iter::Iterator for Lexer<'a> {
|
impl<'a> std::iter::Iterator for Lexer<'a> {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use std::fmt::Display;
|
||||||
|
|
||||||
use crate::loader::Span;
|
use crate::loader::Span;
|
||||||
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Logs {
|
pub struct Logs {
|
||||||
logs: Vec<LogEntry>,
|
logs: Vec<LogEntry>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
automatan::*, dual_enum_serde, dual_struct_serde, loader::{
|
automatan::*,
|
||||||
|
dual_enum_serde,
|
||||||
|
loader::{
|
||||||
ast::TopLevel,
|
ast::TopLevel,
|
||||||
log::{LogEntry, LogSink},
|
log::{LogEntry, LogSink},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
|
@ -119,8 +121,8 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dual_enum_serde!{
|
dual_enum_serde! {
|
||||||
{#[serde(tag = "type")] #[serde(rename_all = "snake_case")]}
|
{#[serde(tag = "type")] #[serde(rename_all = "snake_case")]}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Machine<'a> {
|
pub enum Machine<'a> {
|
||||||
Fa(#[serde(borrow)] fa::Fa<'a>),
|
Fa(#[serde(borrow)] fa::Fa<'a>),
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
fn parse_as_symbol(&mut self, tok: S<T<'a>>) -> S<Symbol<'a>> {
|
fn parse_as_symbol(&mut self, tok: S<T<'a>>) -> S<Symbol<'a>> {
|
||||||
match tok {
|
match tok {
|
||||||
S(T::Tilde, r) => S(Symbol::Epsilon("~"), r),
|
S(T::Tilde, r) => S(Symbol::Epsilon("~"), r),
|
||||||
S(T::Ident(repr@ epsilon!(pat)), r) => S(Symbol::Epsilon(repr), r),
|
S(T::Ident(repr @ epsilon!(pat)), r) => S(Symbol::Epsilon(repr), r),
|
||||||
S(T::Ident(ident), r) => S(Symbol::Ident(ident), r),
|
S(T::Ident(ident), r) => S(Symbol::Ident(ident), r),
|
||||||
S(got, span) => {
|
S(got, span) => {
|
||||||
self.ctx.emit_error(
|
self.ctx.emit_error(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
|
pub fn main() {}
|
||||||
|
|
||||||
pub fn main(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ export type Tm = {
|
||||||
type: "tm";
|
type: "tm";
|
||||||
|
|
||||||
initial_state: State;
|
initial_state: State;
|
||||||
initial_tape: Symbol;
|
blank_symbol: Symbol;
|
||||||
states: Map<State, StateInfo>;
|
states: Map<State, StateInfo>;
|
||||||
symbols: Map<Symbol, SymbolInfo>;
|
symbols: Map<Symbol, SymbolInfo>;
|
||||||
alphabet: Map<Letter, LetterInfo>;
|
alphabet: Map<Letter, LetterInfo>;
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ pub fn lex(input: &str) -> Vec<Tok> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ugly hack to keep single ascii letters non keyworded for user
|
// ugly hack to keep single ascii letters non keyworded for user
|
||||||
Token::Ident(ident) if ident.is_ascii() && ident.len()==1 => Kind::Ident,
|
Token::Ident(ident) if ident.is_ascii() && ident.len() == 1 => Kind::Ident,
|
||||||
Token::Ident(
|
Token::Ident(
|
||||||
epsilon!(pat) | delta_lower!(pat) | sigma_upper!(pat) | gamma_upper!(pat),
|
epsilon!(pat) | delta_lower!(pat) | sigma_upper!(pat) | gamma_upper!(pat),
|
||||||
) => Kind::Keyword,
|
) => Kind::Keyword,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue