// tm.cpp simulate a Turing Machine // // A Turing machine M = (Q, Sigma, Gamma, delta, q0, B, F) // where Q is finite set of states including q0 // Sigma is a finite set of input symbols not including B #b ## // Gamma is a finite set of tape symbols including Sigma and B // delta is a transitions mapping Q X Gamma to Q X Gamma X {L,R,N} // q0 is the initial state // B is blank tape symbol // F is a finite set of final states // // Input Conventions: // // the file name extension is typically .tm // free form plain text input file // typical execution is tm < your_input.tm > 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 five tuple: // from-state input-symbol to-state symbol-to-write move // // at least one transition is required // a typical dummy transition is phi #b phi ## N // // special input-symbol #b for the blank character, // #[ for beginning of tape // #] for end of tape // #e for epsilon transition // // special symbol-to-write #b for blank character // ## for no character to write // // move is L or l for left one space on the tape // R or r for right one space on the tape // N or n for do not move the tape head // anything else becomes "R" // // Anything after the required field(s) is a comment // // if symbol-to-write is "// " then the rest of the line // is a comment and symbol-to-write becomes ## // and move becomes R, as in a DFA subset of a TM. // // if move is "// " then move becomes "N" and // and the rest of the line is treated as a comment // // The input and output tape may contain only printable characters // Use #b to input a blank (space) character on the input tape // If you want a blank on each end of your tape, use #byour-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, input, to-write, tape // note any strangeness // build transition map (note: duplicates means nondeterministic) // initialize tape // 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 tm tm.cpp or cl /GX /ML tm.cpp // ANSI/ISO C++ // release JSS 1/15/00 v1.0 // mod JSS 4/26/01 v1.0.1 allow comments after 'enddef' // mod JSS 12/5/03 V1.1 fix bug caught by g++ 3.2 // mod JSS 5/10/04 V1.2 fix bug it->its, halt_mode #include #include #include #include #include #include using namespace std; static char no_write = '\0'; // unprintable, do not write static char epsilon = '\0'; // unprintable, #e epsilon static char right_end = '\1'; // unprintable, #] off right end of tape static char left_end = '\2'; // unprintable, #[ off left end of tape class transition // 'delta' is the set of these transitions { public: transition(string from_state, char input_symbol, string to_state, char write_symbol, char move_tape) {from=from_state; input=input_symbol; to=to_state; write=write_symbol; move=move_tape;} 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 = staring state set final; // F = set of final states set symbols; // Gamma set of tape symbols set trace; // set of states to trace vector tape; // input tape vector t_table; // delta transitions 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 halted = false; // can not simulate more, halted bool halt_mode = false; // stop upon any '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; // read/write head position char input_char = ' '; // tape character read string next_state; // transition to next state char write_char; // '\0' means no write char move_char; // move 'L' 'R' 'N' 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 ntm simulator, not this tm // 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 move_tape; // character extracted later string write_symbol; // character extracted later 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 #]. right_end string write_str; // temp string for ##, no write char buf[1000]; // temp buffer for comments // 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 cout << "tm.cpp 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, now printed "// " { 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=="#[") input_symbol[0]=left_end; // left end if(input_symbol=="#]") input_symbol[0]=right_end; // right end if(input_symbol=="#e") input_symbol[0]=epsilon; // epsilon cin >> to_state; if(cin.eof()) break; states.insert(to_state); to_states.insert(to_state); write_symbol=string(1, no_write); move_tape="R"; cin >> check; if(cin.eof()) break; if(check!=comm) { write_symbol=check; if(write_symbol=="#b") write_symbol[0]=' '; // blank if(write_symbol=="##") write_symbol[0]=no_write; // no write cin >> check; if(cin.eof()) break; if(check!=comm) { move_tape=check; if(move_tape=="n") move_tape="N"; if(move_tape=="l") move_tape="L"; if(move_tape=="r") move_tape="R"; if(!(move_tape=="L" || move_tape=="R" || move_tape=="N")) { cout << move_tape << " is not a legal move, set to R" << endl; move_tape="R"; } } } cin.ignore(255,'\n'); // delete trailing comments t_table.push_back(transition(from_state, input_symbol[0], to_state, write_symbol[0], move_tape[0])); } // 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, "", no_write, 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) { nondeterministic = true; cout << " duplicate transition" << endl; } prev_from=x->from; prev_input=x->input; } } else { cout << "No transitions (five tuples)" << endl; no_simulate=true; } if(nondeterministic) { cout << "nondeterministic 'alpha' transition table" << endl; cout << "either eliminate duplicate transitions or use ntm 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" or "//" is allowed if(!cin.eof() && keyword=="//") { cin.getline(buf, 1000, '\n'); cout << keyword << buf << endl; continue; } if(!cin.eof() && keyword==key_tape) { cin >> initial_tape; if(initial_tape=="//") initial_tape = ""; // null tape cin.ignore(255,'\n'); } else { cout << endl << " No more input tapes. Stopping." << endl; return 0; } } // 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=tape.size()) { tape_position--; // can not move right of right_end cout << "attemp to move right of right_end of tape" << endl; input_char=right_end; break; // out of 'step' loop } } state=next_state; input_char = tape[tape_position]; // may be left_end or right_end } // step limit loop // final messages to the user if(halted) { cout << "tm halted after " << step << " steps, at " << state << " with output:" << endl ; for(it=(tape.begin()+1); it!=(tape.end()-1); it++) cout << *it; cout << endl; cout << endl; cout << endl; } else if(accepted) { cout << "tape accepted after " << step << " steps, at " << state << endl ; cout << endl ; cout << endl ; } else { cout << "tape rejected after " << step << " steps, at " << state << endl; cout << endl ; cout << endl ; if(input_char==left_end) cout << "Off left end of tape" << endl; if(input_char==right_end) cout << "Off right end of tape" << endl; } initial_tape = ""; accepted = false; }while(!cin.eof() && have_enddef); // close out tape read 'do' return 0; } // end of main // for outputting objects of class transition ostream &operator<<(ostream &stream, transition trans) { string input_token=string(1,trans.input); if(trans.input==epsilon) input_token=string("#e"); if(trans.input==right_end) input_token=string("#]"); if(trans.input==left_end) input_token=string("#["); if(trans.input==' ') input_token=string("#b"); string write_token=string(1,trans.write); if(trans.write==no_write) write_token=string("##"); if(trans.write==' ') write_token=string("#b"); string move_token=string(1,trans.move); stream << trans.from << '\t' << input_token << '\t' << trans.to << '\t' << write_token << '\t' << move_token ; return stream; } // end tm.cpp