// dfa.cpp simulate a Deterministic Finite Automata // // Machine definition of a DFA M = (Q, Sigma, alpha, q0, F) // where Q is set of states // Sigma is input tape alphabet // alpha is transition table // q0 is the starting state // F is the set of final states // // Input Conventions: // // the file name extension is typically .dfa // free form plain text input file // typical execution is dfa < your_input.dfa > your_output.out // // A comment line has "// " as the first three characters // A line may end with a comment using whitespace "//" // Anything after whitespace after last expected input is a comment // // There are only a few keywords: // start, final, halt, trace, limit, enddef and tape // any line not starting with these keywords is a transition // 'enddef' is only needed when more than one 'tape' is to be read // // there is no such thing as a continuation line // // A transition is a three tuple: // from-state input-symbol to-state // // special input-symbol #b for the blank character, // #e for epsilon transition // // at least one transition is required, // the typical dummy transition is phi #b phi // // Anything after the required field(s) is a comment // // Use #b to input a blank (space) character on the input tape // If you want a blank on the end of your tape, use your-stuff#b // The initial position will point to your first tape character // #] will be appended as a right_end character // #[ will be appended as a left_end character // // Method: accumulate states, do consistency checking // accumulate symbols, note any strangeness // build transition map (note: duplicates means nondeterministic) // run from start-state // trace as user requests // accept or reject (also halt feature) // // This is one of a series of automata simulators // dfa deterministic finite automata (with epsilon transitions and halt) // nfa nondeterministic finite automata // tm Turing machine, deterministic (recognizer and function computation) // ntm nondeterministic Turing machine (language recognizer only) // // compile with GNU g++ 2.8.1 or later or Microsoft 6.0 or later // g++ -o dfa dfa.cpp or cl /GX /ML dfa.cpp // ANSI/ISO C++ // release JSS 1/15/00 v1.0 // mod JSS 12/5/03 v1.1 fix bug found by g++ 3.2 #include #include #include #include #include #include using namespace std; class transition // an entry in 'alpha' transition_table { public: transition(string from_state, char input_symbol, string to_state) {from=from_state; input=input_symbol; to=to_state;} static bool smaller(transition a, transition b) { if(a.fromb.from) return false; else return a.input output-file { // primary objects set states; // Q =all named states set from_states; // source states for checking set to_states; // target states for checking string start = ""; // q0 = starting state set final; // F = set of final states set symbols; // Sigma = set of tape symbols set trace; // states to trace vector tape; // input tape vector t_table; // alpha = transition table typedef pair c_index; // to make t_index map > t_index; // t_table index // primary iterators vector::const_iterator x; // for t_table int tx; // for t_table set::const_iterator iter; // for sets set::const_iterator iter2; // for sets vector::iterator it; // for tape string::iterator its; // for string // control variables int limit = 10000; // max number of transitions (user settable) int step; // transition step bool halt_mode = false; // stop upon entering a 'final' state bool accepted = false; // reached a final state and input right-end bool no_simulate = false; // set true by various errors string state; // present state int tape_position; // tape position char input_char = ' '; // tape character read string next_state = " "; // transition to next state int i; // loop index string prev_from = " "; // for detecting nondeterministic char prev_input = ' '; // for detecting nondeterministic bool have_enddef = false; // may have to read "tape" bool nondeterministic = false; // use nfa simulator, not this dfa // input variables string keyword; // for inputting candidate keyword string from_state; // for inputting one from_state string input_symbol; // character extracted later string to_state; // for inputting one to-state string final_state; // for inputting one final or halt state string trace_state; // for inputting one trace_state string initial_tape =""; // check for #b (may read more than 1) string check; // check for // that ends data on a line string input_str; // temp string for #] char buf[256]; // for printing comments in input file // keywords and special symbols string key_start = "start"; // followed by a state string key_tape = "tape"; // followed by a string, may have #b string key_final = "final"; // followed by a final state name string key_halt = "halt"; // followed by a final state name, halt mode string key_trace = "trace"; // followed by "all" or a state string key_limit = "limit"; // followed by an integer string key_enddef= "enddef"; // only a "tape" may follow this string comm = "//"; // start comment string empty = ""; // empty string char epsilon = '\0'; // unprintable, #e epsilon char right_end = '\1'; // unprintable, #] off right end of tape char left_end = '\2'; // unprintable, #[ off left end of tape cout << "dfa.cpp v1.1 starting" << endl; while(!cin.eof()) // main input loop { cin >> keyword; if(cin.eof()) break; if(keyword==key_start) { if(start!=empty) cout << "Over writing a previous start state." << endl; cin >> start; states.insert(start); if(start=="//") cout << "start looks like a comment?" << endl; cin.ignore(255,'\n'); continue; } else if(keyword==key_tape) { cin >> initial_tape; if(initial_tape=="//") initial_tape=""; cin.ignore(255,'\n'); continue; } else if(keyword==key_final) { cin >> final_state; final.insert(final_state); if(final_state=="//") cout << "final looks like a comment?" << endl; cin.ignore(255,'\n'); continue; } else if(keyword==key_halt) { halt_mode = true; // halt upon entering any final state cin >> final_state; if(final_state!=comm && final_state!=empty) { // allow stand alone "halt" just to set flag final.insert(final_state); } cin.ignore(255,'\n'); continue; } else if(keyword==key_trace) { cin >> trace_state; trace.insert(trace_state); if(trace_state=="//") cout << "trace state looks like a comment?" << endl; cin.ignore(255,'\n'); continue; } else if(keyword==key_limit) { cin >> limit; cin.ignore(255,'\n'); continue; } else if(keyword==key_enddef) // stop inputting { // now may only read "tape" cin.ignore(255,'\n'); have_enddef = true; break; } else if(keyword=="//") // skip comment, print added "// " { cin.getline(buf, 255, '\n'); cout << keyword << buf << endl; // cin.ignore(255,'\n'); continue; } else { from_state = keyword; states.insert(from_state); from_states.insert(from_state); } cin >> input_symbol; if(cin.eof()) break; if(input_symbol=="#b") input_symbol[0]=' '; // a space if(input_symbol=="#e") input_symbol[0]=epsilon; // epsilon if(input_symbol=="#]") input_symbol[0]=right_end; // right end cin >> to_state; if(cin.eof()) break; states.insert(to_state); to_states.insert(to_state); cin.ignore(255,'\n'); // delete trailing comments t_table.push_back(transition(from_state, input_symbol[0], to_state)); } // end main input loop cout << "reading input finished." << endl; // print primary data objects, check for errors and warnings if(states.size()==0) { cout << "No states input prevent simulation. Stopping." << endl; return 1; } cout << "start state " << start << endl; cout << "states:"; for(iter=states.begin(); iter!=states.end(); iter++) cout << *iter << " "; cout << endl; iter = from_states.find(start); // check that start is a legal state if(iter==states.end()) { cout << start << " is not a legal starting state." << endl; no_simulate=true; } cout << endl; cout << "final states:"; if(final.size()==0) { cout << " No final states" << endl; } else if(to_states.size()!=0) { for(iter=final.begin(); iter!=final.end(); iter++) cout << *iter << " "; cout << endl; for(iter=final.begin(); iter!=final.end(); iter++) // check final states { iter2 = to_states.find(*iter); if(iter2==to_states.end() && (*iter!=start)) { cout << *iter << " is not a valid final state." << endl; no_simulate=true; } } cout << endl; } if(trace.size()==1 && *trace.begin() == "all") trace = states; cout << "trace states:"; if(trace.size()==0) { cout << "No states to trace" << endl; } else { for(iter=trace.begin(); iter!=trace.end(); iter++) cout << *iter << " "; cout << endl << endl; for(iter=trace.begin(); iter!=trace.end(); iter++) // check trace states { iter2 = states.find(*iter); if(iter2==states.end()) { cout << *iter << " is not a valid trace state." << endl; } } cout << endl; } if(t_table.size()!=0) { t_table.push_back(transition("", epsilon, "")); // error catching node sort(t_table.begin(), t_table.end(), transition::smaller ); cout << "Sorted transition table:" << endl; for(x=(t_table.begin()+1); x!=t_table.end(); x++) { cout << *x << endl; if(x->from==prev_from && (x->input==prev_input || prev_input==epsilon)) { nondeterministic = true; cout << " duplicate transition" << endl; } prev_from=x->from; prev_input=x->input; } } else { cout << "No transitions (three tuples)" << endl; no_simulate=true; } if(nondeterministic) { cout << "nondeterministic 'alpha' transition table" << endl; cout << "either eliminate duplicate transitions or use nfa simulator" << endl; return 1; } if(no_simulate) { cout << "Errors in input prevent simulation. Stopping." << endl; return 1; } // build map for state+input_character to transition table index for(tx=0; tx> keyword; // only "tape" is allowed if(!cin.eof() && keyword==key_tape) { cin >> initial_tape; if(initial_tape=="//") initial_tape = ""; // null tape cin.ignore(255,'\n'); } else { cout << " No more input tapes. Stopping." << endl; return 1; } } // clean up initial_tape into tape tape.clear(); // may come around again tape.push_back(left_end); // force known beginning of tape for(its=initial_tape.begin(); its!=initial_tape.end(); its++) { if((its+1)!=initial_tape.end()) { if(*its=='#' && *(its+1)=='b') { tape.push_back(' '); its++; } else { tape.push_back(*its); // allows '#' sometimes } } else { tape.push_back(*its); } } tape.push_back(right_end); // force known end of tape tape_position = 1; // start past #[ on first user character cout << endl << "tape = "; for(it=(tape.begin()+1); it!=(tape.end()-1); it++) cout << *it; cout << endl; // do not print the left_end and right_end state=start; // state set to next_state at bottom of loop input_char = tape[tape_position]; // input_char set also at bottom for(step=1; step