module Turing // ************************************************************************************************** // // This program is a Turing machine interpreter and programming environment. // // The program has been written in Clean 2.0 and uses the Clean Standard Object I/O library 1.2.2 // // ************************************************************************************************** import StdEnv, StdIO import tm, showtm, tmdialog, tmfile, Help from StdFunc import seq Speed1 :== ticksPerSecond / 3 Speed2 :== ticksPerSecond / 6 Speed3 :== ticksPerSecond / 12 Speed4 :== ticksPerSecond / 20 Speed5 :== 0 Start :: *World -> *World Start world # (tmids,world) = openTmIds world # (ids, world) = openIds 3 world = startTuring tmids ids world startTuring :: TmIds [Id] *World -> *World startTuring tmids ids world = startIO MDI initTuring initialise [ ProcessClose DoQuit , ProcessOpenFiles DoOpenFiles ] world where [runItemId,contItemId,timerID:_] = ids {windowID,tapeWdID,fileMenuId,saveItemId,machineMenuId,stepItemId,haltItemId} = tmids initTuring = { tmstate = { turing = { transitions = [] , tape = { content = "" , head = 0 } , state = "" } , transition = 0 , command = None } , name = "" , delay = Speed3 , saved = True , tmids = tmids } initialise :: (PSt Tm) -> PSt Tm initialise pst=:{ls={tmstate=tm=:{turing={tape}}}} # (error,pst) = openMenu undef file pst | error<>NoError = abort "Turing could not open file menu" # (error,pst) = openMenu undef machine pst | error<>NoError = abort "Turing could not open machine menu" # (error,pst) = openWindow undef trswd pst | error<>NoError = abort "Turing could not open transitions window" # (error,pst) = openWindow undef tapewd pst | error<>NoError = abort "Turing could not open tape window" # (error,pst) = openTimer undef timer pst | error<>NoError = abort "Turing could not open timer" | otherwise = pst where file = Menu "File" ( MenuItem "New" [MenuShortKey 'N',MenuFunction (noLS DoNew)] :+: MenuItem "Open..." [MenuShortKey 'O',MenuFunction (noLS DoOpen)] :+: MenuItem "Save" [MenuShortKey 'S',MenuFunction (noLS DoSave),MenuId saveItemId,MenuSelectState Unable] :+: MenuItem "Save As..." [MenuFunction (noLS DoSaveAs)] :+: MenuSeparator [] :+: MenuItem "Help..." [MenuShortKey 'H',MenuFunction (noLS Help)] :+: MenuSeparator [] :+: MenuItem "Quit" [MenuShortKey 'Q',MenuFunction (noLS DoQuit)] ) [ MenuId fileMenuId ] machine = Menu "Machine" ( MenuItem "Step" [MenuShortKey 'T',MenuFunction (noLS DoStep), MenuId stepItemId,MenuSelectState Unable] :+: MenuItem "Run" [MenuShortKey 'R',MenuFunction (noLS DoRun), MenuId runItemId, MenuSelectState Unable] :+: MenuItem "Halt" [MenuShortKey '.',MenuFunction (noLS DoHalt), MenuId haltItemId,MenuSelectState Unable] :+: MenuItem "Continue" [MenuShortKey ',',MenuFunction (noLS DoContinue),MenuId contItemId,MenuSelectState Unable] :+: MenuSeparator [] :+: SubMenu "Speed" ( RadioMenu [ ("Very Slow",Nothing,Just '1',noLS (SetDelay Speed1)) // VerSId , ("Slow" ,Nothing,Just '2',noLS (SetDelay Speed2)) // SlowId , ("Normal" ,Nothing,Just '3',noLS (SetDelay Speed3)) // NormId , ("Fast" ,Nothing,Just '4',noLS (SetDelay Speed4)) // FastId , ("Very Fast",Nothing,Just '5',noLS (SetDelay Speed5)) // VerFId ] 3 [] ) [] ) [ MenuId machineMenuId ] trswd = Window "Turing Machine" NilLS [ WindowId windowID , WindowViewDomain {zero & corner2={x=MaxX,y=265}} , WindowViewSize {w=500,h=265} , WindowLook True (tmLook tm) , WindowMouse tmMouseFilter Able (noLS1 EditTransitions) , WindowClose (noLS DoQuit) , WindowHScroll (stdScrollFunction Horizontal 24) , WindowVScroll (stdScrollFunction Vertical 8) ] tapewd = Window "Tape" NilLS [ WindowId tapeWdID , WindowViewDomain {zero & corner2={x=MaxX,y=92}} , WindowViewSize {w=500,h=92} , WindowLook True (tpLook tape) , WindowMouse tpMouseFilter Able (noLS1 EditTape) , WindowClose (noLS DoQuit) , WindowHScroll (stdScrollFunction Horizontal 24) , WindowVScroll (stdScrollFunction Vertical 8) , WindowPos (Below windowID,zero) ] timer = Timer Speed3 NilLS [TimerId timerID,TimerSelectState Unable,TimerFunction (noLS1 TimerStep)] // Open a new empty Turing machine. DoNew :: (PSt Tm) -> PSt Tm DoNew pst=:{ls={saved}} | saved = MakeNewTuring pst | otherwise = SaveBeforeClose "opening a new Turing machine" MakeNewTuring pst MakeNewTuring :: (PSt Tm) -> PSt Tm MakeNewTuring pst=:{ls=tm=:{delay},io} # io = setWindowLook tapeWdID True (True,tpLook inittape) io # io = setWindowLook windowID True (True,tmLook inittmstate) io # io = setWindowTitle windowID "Turing Machine" io # io = disableMenuElements [saveItemId,stepItemId,runItemId,haltItemId,contItemId] io = {pst & ls=inittm,io=io} where inittape = {content="",head=0} inittmstate = { turing = { transitions = [] , tape = inittape , state = "" } , transition = 0 , command = None } inittm = {tm & tmstate = inittmstate , name = "" , delay = delay , saved = True } // Save the Turing machine. DoSave :: (PSt Tm) -> PSt Tm DoSave pst=:{ls=tm=:{tmstate={turing},name},io} # (success,io) = WriteTuringToFile turing name io | success # pst = {pst & ls={tm & saved=True},io=io} # pst = appPIO (disableMenuElements [saveItemId]) pst = pst | otherwise = Alert "The Turing machine has not been saved." "The file could not be opened." {pst & io=io} // Save the Turing machine in a new file. DoSaveAs :: (PSt Tm) -> PSt Tm DoSaveAs pst=:{ls=tm=:{name,tmstate={turing}}} # (fname,pst) = selectOutputFile "Save T.M. As:" (RemovePath name) pst | isNothing fname = pst # fname = fromJust fname | RemovePath fname==HelpFile = Alert "The Turing machine cannot be saved to" ("the help file \'"+++HelpFile+++"\'.") pst # (success,pst) = WriteTuringToFile turing fname pst | not success = Alert "The Turing machine has not been saved." "The file could not be opened." pst # pst = appPIO (setWindowTitle windowID (RemovePath fname)) pst # pst = appPIO (disableMenuElements [saveItemId]) pst | otherwise = {pst & ls={tm & name=fname,saved=True}} // Load a Turing machine from a file. DoOpen :: (PSt Tm) -> PSt Tm DoOpen pst=:{ls={saved}} | saved = EvtOpenTuring pst | otherwise = SaveBeforeClose "opening an other Turing machine" EvtOpenTuring pst where EvtOpenTuring :: (PSt Tm) -> PSt Tm EvtOpenTuring pst # (filename,pst) = selectInputFile pst | isNothing filename = pst | otherwise = OpenTuringFile (fromJust filename) pst // DoOpenFiles opens the first file of the argument list. DoOpenFiles :: [String] (PSt Tm) -> PSt Tm DoOpenFiles names pst=:{ls={saved}} | saved = OpenTuringFile (hd names) pst | otherwise = SaveBeforeClose "opening an other Turing machine" (OpenTuringFile (hd names)) pst // OpenTuringFile parses the file found at the path argument and if successful opens it. OpenTuringFile :: String (PSt Tm) -> PSt Tm OpenTuringFile name pst=:{ls=tm} # fname = RemovePath name fstring = " \'"+++fname+++"\'" | fname==HelpFile = Alert ("The help file"+++fstring) "cannot be opened as a T.M." pst # ((status,turing),pst) = ReadTuring name pst | status==0 # tmstate = {turing=turing,transition=0,command=None} # pst = appListPIO [ enableMenuElements [runItemId,stepItemId] , disableMenuElements [saveItemId] , setWindowLook tapeWdID True (True,tpLook turing.tape) , setWindowLook windowID True (True,tmLook tmstate) , setWindowTitle windowID (RemovePath name) ] pst = {pst & ls={tm & tmstate=tmstate,name=name,saved=True}} | otherwise # (msg1,msg2) = if (status>0) ("Parse error in line "+++toString status,"of file"+++fstring+++".") (if (status==(-1)) ("Unexpected end of file",fstring+++".") ("The file"+++fstring,"could not be opened.")) = Alert msg1 msg2 pst // The Help command. Help :: (PSt Tm) -> PSt Tm Help pst = showHelp HelpFile pst // Let the Turing machine do one step (transition). DoStep :: (PSt Tm) -> PSt Tm DoStep pst=:{ls=tm=:{tmstate=tmstate=:{turing={tape={head},state},transition}},io} | state=="halt" || state=="error" = pst # tmstate = Step tmstate tm = {tm & tmstate=tmstate} newtrn = tmstate.transition newstate = tmstate.turing.state newcom = tmstate.command newtape = tmstate.turing.tape # io = setWindowLook windowID False (True,tmLook tmstate) io # io = appWindowPicture windowID (ShowNextState newstate o ShowTransition transition newtrn) io # io = setWindowLook tapeWdID False (True,tpLook newtape) io # io = appWindowPicture tapeWdID (ShowNewTape newcom head) io | newstate<>"halt" && newstate<>"error" = {pst & ls={tm & tmstate=tmstate},io=io} | otherwise # io = disableMenuElements [stepItemId,haltItemId,contItemId] io = {pst & ls={tm & tmstate=tmstate},io=io} // Let the T.M. run until the haltstate is reached. DoRun :: (PSt Tm) -> PSt Tm DoRun pst=:{ls=tm=:{tmstate={turing}},io} # io = disableWindowMouse tapeWdID io # io = disableWindowMouse windowID io # io = enableMenuElements [haltItemId] io # io = disableMenuElements [stepItemId,runItemId] io # io = disableMenus [fileMenuId] io # io = appWindowPicture tapeWdID EraseError io # io = enableTimer timerID io = {pst & ls={tm & tmstate={tm.tmstate & turing={turing & state="S"}}},io=io} // Halt a running T.M. DoHalt :: (PSt Tm) -> PSt Tm DoHalt pst=:{io} # io = enableWindowMouse tapeWdID io # io = enableWindowMouse windowID io # io = enableMenuElements [stepItemId,runItemId,contItemId] io # io = enableMenus [fileMenuId] io # io = disableTimer timerID io = {pst & io=io} // Continue a halted T.M. DoContinue :: (PSt Tm) -> PSt Tm DoContinue pst=:{io} # io = disableWindowMouse tapeWdID io # io = disableWindowMouse windowID io # io = disableMenuElements [stepItemId,runItemId] io # io = disableMenus [fileMenuId] io # io = enableTimer timerID io = {pst & io=io} // Set the speed (delay) of a (possibly running) T.M. SetDelay :: Int (PSt Tm) -> PSt Tm SetDelay delay pst=:{ls=tm,io} = {pst & ls={tm & delay=delay},io=setTimerInterval timerID delay io} // Quit the program. DoQuit :: (PSt Tm) -> PSt Tm DoQuit pst=:{ls={saved}} | saved = closeProcess pst | otherwise = SaveBeforeClose "quitting" closeProcess pst // When a MouseDown event occurs (filtered by tmMouseFilter) the T.M. can be edited (handled by EditTransitions). tmMouseFilter :: MouseState -> Bool tmMouseFilter (MouseDown _ _ _) = True tmMouseFilter _ = False EditTransitions :: MouseState (PSt Tm) -> PSt Tm EditTransitions (MouseDown mpos _ _) pst=:{ls=tm=:{tmstate={turing={transitions}}},io} | ontrans = AlterTransition transnr pst | onstate = AlterState pst | otherwise = pst where (nr,ontrans,onstate)= ClickedInWindow mpos lasttrans = NrOfTransitions transitions transnr = if (nr>lasttrans) lasttrans nr // When a MouseDown event occurs (filtered by tpMouseFilter) the head can be moved or the tape content changed (handled by EditTape). tpMouseFilter :: MouseState -> Bool tpMouseFilter (MouseDown _ _ _) = True tpMouseFilter _ = False EditTape :: MouseState (PSt Tm) -> PSt Tm EditTape (MouseDown mpos {commandDown} _) pst=:{ls=tm=:{tmstate=tmst=:{turing}},io} | not ontape = pst | commandDown # tape = MoveHead newpos turing.tape # (frame,io)= getWindowViewFrame tapeWdID io # io = setWindowLook tapeWdID False (True,tpLook tape) io # io = appWindowPicture tapeWdID (ShowHeadMove {tape & head=oldpos} newpos frame.corner1.x frame.corner2.x) io = {pst & ls={tm & tmstate={tmst & turing={turing & tape=tape}}},io=io} | otherwise = AlterCell (min newpos (NrOfCells turing.tape.content)) pst where oldpos = turing.tape.head (newpos,ontape) = ClickedInTapeWd mpos // The window update and activate functions. tmLook :: TmState SelectState UpdateState *Picture -> *Picture tmLook {turing={transitions,state},transition} _ _ picture # picture = SetTuringFont picture # picture = ShowTransitions transitions state picture # picture = ShowTransition transition transition picture = picture tpLook :: Tape SelectState UpdateState *Picture -> *Picture tpLook tape selectState updState=:{updArea} picture # picture = SetTuringFont picture # picture = seq [ShowTapePart tape updRect.corner1.x updRect.corner2.x o unfill updRect \\ updRect<-updArea] picture = picture // The step function for the Timer device (used by the Run command). TimerStep :: NrOfIntervals (PSt Tm) -> PSt Tm TimerStep _ pst=:{ls=tm=:{tmstate={turing={state}}},io} | state<>"halt" && state<>"error" = DoStep pst | otherwise # io = disableTimer timerID io # io = enableWindowMouse tapeWdID io # io = enableWindowMouse windowID io # io = disableMenuElements [haltItemId,contItemId] io # io = enableMenuElements [runItemId] io # io = enableMenus [fileMenuId] io = {pst & io=io}