Skip to content

Commit

Permalink
Either everywhere and cleaning (#3)
Browse files Browse the repository at this point in the history
* renaming TransitionObject to Transitionstruct

* Either is spreading

* moving stuff around / cleaning

* Machine fields are not prefixed anymore

* parseMachine is no a simpler eitherDecode.pack

* Parser.hs removed as now useless

* cleaning ok
  • Loading branch information
Karocyt authored Feb 16, 2021
1 parent 4738235 commit 7aafc4f
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 140 deletions.
28 changes: 28 additions & 0 deletions Examples/Broken/bad_type.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name" : "unary_sub",
"alphabet": [ "1", ".", "-", "=" ],
"blank" : ["."],
"states" : [ "scanright", "eraseone", "subone", "skip", "HALT" ],
"initial" : "scanright",
"finals" : [ "HALT" ],
"transitions" : {
"scanright": [
{ "read" : ".", "to_state": "scanright", "write": ".", "action": "RIGHT"},
{ "read" : "1", "to_state": "scanright", "write": "1", "action": "RIGHT"},
{ "read" : "-", "to_state": "scanright", "write": "-", "action": "RIGHT"},
{ "read" : "=", "to_state": "eraseone" , "write": ".", "action": "LEFT" }
],
"eraseone": [
{ "read" : "1", "to_state": "subone", "write": "=", "action": "LEFT"},
{ "read" : "-", "to_state": "HALT" , "write": ".", "action": "LEFT"}
],
"subone": [
{ "read" : "1", "to_state": "subone", "write": "1", "action": "LEFT"},
{ "read" : "-", "to_state": "skip" , "write": "-", "action": "LEFT"}
],
"skip": [
{ "read" : ".", "to_state": "skip" , "write": ".", "action": "LEFT"},
{ "read" : "1", "to_state": "scanright", "write": ".", "action": "RIGHT"}
]
}
}
28 changes: 28 additions & 0 deletions Examples/Broken/missing_field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name" : "unary_sub",
"alphabet": [ "1", ".", "-", "=" ],
"blank" : ".",
"states" : [ "scanright", "eraseone", "subone", "skip", "HALT" ],
"initial" : "scanright",
"finals" : [ "HALT" ],
"transitions" : {
"scanright": [
{ "read" : ".", "fdf": "scanright", "write": ".", "action": "RIGHT"},
{ "read" : "1", "to_state": "scanright", "write": "1", "action": "RIGHT"},
{ "read" : "-", "to_state": "scanright", "write": "-", "action": "RIGHT"},
{ "read" : "=", "to_state": "eraseone" , "write": ".", "action": "LEFT" }
],
"eraseone": [
{ "read" : "1", "to_state": "subone", "write": "=", "action": "LEFT"},
{ "read" : "-", "to_state": "HALT" , "write": ".", "action": "LEFT"}
],
"subone": [
{ "read" : "1", "to_state": "subone", "write": "1", "action": "LEFT"},
{ "read" : "-", "to_state": "skip" , "write": "-", "action": "LEFT"}
],
"skip": [
{ "read" : ".", "to_state": "skip" , "write": ".", "action": "LEFT"},
{ "read" : "1", "to_state": "scanright", "write": ".", "action": "RIGHT"}
]
}
}
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,74 @@ stack exec a-machine-exe desc.json tape
where:
- `desc.json` is a json encoded file containing a valid machine description
- `tape` is a string of instructions from the machine alphabet

# Notepad:

The json fields are defined as follows:
- name: The name of the described machine

- alphabet: Both input and work alphabet of the machine merged into a single alphabet for simplicity’s sake, including the blank character. Each
character of the alphabet must be a string of length strictly equal to 1.

- blank: The blank character, must be part of the alphabet, must NOT be
part of the input.

- states: The exhaustive list of the machine’s states names.

- initial: The initial state of the machine, must be part of the states list.

- finals: The exhaustive list of the machine’s final states. This list must be a
sub-list of the states list.

- transitions: A dictionnary of the machine’s transitions indexed by state
name. Each transition is a list of dictionnaries, and each dictionnary
describes the transition for a given character under the head of the
machine. A transition is defined as follows:

- read: The character of the machine’s alphabet on the tape under the
machine’s head.

- to_state: The new state of the machine after the transition is done.

- write: The character of the machine’s alphabet to write on the tape
before moving the head.

- action: Movement of the head for this transition, either LEFT, or
RIGHT.


State is composed of:
- tape
- position
- nextTransition

Tape is an alias for String
Move is (+1) or (-1) -- bounds checks in type ?

Transition is of type State -> Either String State
Behing the scenes, Transition is a partially applied GenericTransition of type
(read Char -> write Char -> Move -> to_state String -> currState State -> transitionsList [Transition]) -> newState State
we'll need to first generate partially applied funcs without transitionsList then apply transitionList once all transitions are loaded

Machine should have:
- name: String
- alphabet: [Char]
- blank: Char
- finals: [String]
- transitions: Map (String Char) Transition

Runner is a tail call stopping when currTransition is in finals

all funcs return Either String a
all func are applied with fmap and consorts, passing errors all along
main check the Either for an error String or print the final tape

FLOW:
BasicCheckArgs => buildMachine => runMachine
might need an intermediary layer to avoid the IO context in BuildMachine
BuildMachine will need heavy constructors
BasicCheck args could disappear with even heavier Machine smart constructors
is managing missing tape with Either too much ?
`json` package might sound more "standard library" than `aeson`

To return a Either String Machine, all fields might need to be their own types, returning Either in their constructors ?
5 changes: 2 additions & 3 deletions a-machine.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cabal-version: 1.12
--
-- see: https://github.com/sol/hpack
--
-- hash: de6a899c5247e1faf52e7e16818a5f4fe4c9a6da4bc1937a0b9f1c22c005eda5
-- hash: 6daea2527be2f9b0d068d105f005ab225593979b931c1019c4b1af07de9bcafb

name: a-machine
version: 0.1.0.0
Expand All @@ -28,7 +28,6 @@ source-repository head
library
exposed-modules:
Machine
Parser
Runner
other-modules:
Paths_a_machine
Expand All @@ -42,7 +41,7 @@ library
, unordered-containers
default-language: Haskell2010

executable a-machine-exe
executable a-machine
main-is: Main.hs
other-modules:
Paths_a_machine
Expand Down
54 changes: 38 additions & 16 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,48 @@ module Main where

import System.Environment (getArgs)
import System.IO (readFile)
import Parser
import Control.Exception (try, SomeException)
import Data.ByteString.Lazy.Char8 (pack)
import Data.Aeson (eitherDecode)
import Machine
import Runner

-- both args and content might need to be IO (Either String a) ?
-- I broke my teeth on readFile, my best being IO (Either error Bytestring) at some point
usage :: String
usage = "Usage: ./a-machine desc.json tape\nwhere:\n\t- 'desc.json' is a json encoded file containing a valid machine description\n\t- 'tape' is a string of instructions from the machine alphabet"

-- main :: IO ()
main = do
putStrLn "-- BEGIN ----------------------------------"
args <- (getArgs >>= processArgs) -- parenthesis for aesthetics
putStrLn "Arguments checked"
let tape = args !! 1
content <- readFile $ head args
putStrLn "Description read"
-- print content
processArgs :: [String] -> Either String [String]
processArgs xs | length xs /= 2 = Left usage
| otherwise = Right xs

eitherRead :: String -> IO (Either String String)
eitherRead filename = do
res <- try (readFile filename) :: IO (Either SomeException String)
case res of
Left ex -> pure $ Left ("Unable to read " ++ filename ++ " without black magic:\n\t" ++ show ex ++ "\n" ++ usage)
Right content -> pure $ Right content

buildMachine :: Either String [String] -> IO (Either String Machine)
buildMachine (Left str) = pure $ Left str
buildMachine (Right args) = do
let filename = head args
content <- eitherRead filename
pure $ content >>= eitherDecode.pack

case (parseMachine content) of
Left str -> print str
Right m -> putStrLn $ runMachine tape m
realMain :: Either String Machine -> Either String [String] -> String
realMain (Left err1) _ = err1
realMain _ (Left err2) = err2
realMain (Right machine) (Right args) = case ( (buildState (args !! 1) $ initial machine) >>= (runMachine machine) ) of
Left str -> str
Right last_state -> tape last_state

main :: IO ()
main = do
putStrLn "----- BEGIN --------------------------------------------"
-- IO stuff stays in main then byebye IO
args <- processArgs <$> getArgs
machine <- buildMachine args

putStrLn "-- END ------------------------------------"
-- realMain returns the last tape or an error message
putStrLn $ realMain machine args

putStrLn "----- END (All exceptions/errors handled properly) -----"
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ library:
source-dirs: src

executables:
a-machine-exe:
a-machine:
main: Main.hs
source-dirs: app
ghc-options:
Expand Down
Loading

0 comments on commit 7aafc4f

Please sign in to comment.