diff --git a/README.md b/README.md index 27f2774..f40d0a7 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,4 @@ solution. - [Day 5](src/Years/Y2015/Day5.md) - [Day 6](src/Years/Y2015/Day6.md) - [Day 7](src/Years/Y2015/Day7.md) + - [Day 8](src/Years/Y2015/Day8.md) diff --git a/src/Years/Y2015.md b/src/Years/Y2015.md index 1b1e47f..d3be367 100644 --- a/src/Years/Y2015.md +++ b/src/Years/Y2015.md @@ -14,6 +14,7 @@ import Years.Y2015.Day4 import Years.Y2015.Day5 import Years.Y2015.Day6 import Years.Y2015.Day7 +import Years.Y2015.Day8 --> # Days @@ -66,6 +67,12 @@ y2015 = MkYear 2015 [ , day7 ``` +## [Day 8](Y2015/Day8.md) + +```idris + , day8 +``` + ```idris ] ``` diff --git a/src/Years/Y2015/Day8.md b/src/Years/Y2015/Day8.md new file mode 100644 index 0000000..daed58e --- /dev/null +++ b/src/Years/Y2015/Day8.md @@ -0,0 +1,176 @@ +# Year 2015 Day 8 + + + +```idris +import Data.String +import Data.Vect +import Data.Either +``` + +## Parsing + +Effect alias for a parser + +```idris +Parser : List (Type -> Type) +Parser = + [Choose, State (List Char), Except String] +``` + +Get the next character from the state, modifying it in place + +```idris +getNext : Eff Parser Char +getNext = do + chars <- get + case chars of + [] => throw "End of Input" + (x :: xs) => do + put xs + pure x +``` + +Parse a quote +```idris +quote : Eff Parser () +quote = do + char <- getNext + if char == '"' + then pure () + else throw "Expected quote, found: \{show char}" +``` + +Parse a char that's not a quote or an escape character + +```idris +normal : Eff Parser Char +normal = do + char <- getNext + if any (char ==) (the (List _) ['\\', '"']) + then throw "Tried to parse a special character as normal \{show char}" + else pure char +``` + +Parse a hex digit + +```idris +hexDigit : Eff Parser Int +hexDigit = do + char <- getNext + case toLower char of + '0' => pure 0x0 + '1' => pure 0x1 + '2' => pure 0x2 + '3' => pure 0x3 + '4' => pure 0x4 + '5' => pure 0x5 + '6' => pure 0x6 + '7' => pure 0x7 + '8' => pure 0x8 + '9' => pure 0x9 + 'a' => pure 0xa + 'b' => pure 0xb + 'c' => pure 0xc + 'd' => pure 0xd + 'e' => pure 0xe + 'f' => pure 0xf + _ => throw "Invalid hex digit: \{show char}" +``` + +Parse an escaped character + +```idris +escaped : Eff Parser Char +escaped = do + escape <- getNext + if escape == '\\' + then do + first <- getNext + case first of + '\\' => pure '\\' + '"' => pure '"' + 'x' => do + [x, y] : Vect _ Int <- sequence [hexDigit, hexDigit] + -- Fails to compile + -- pure . chr $ x * 0x10 + y + -- Compiles + pure . (const 'a') $ x * 0x10 + y + _ => throw "Invalid Escape Character: \{show first}" + else throw "Tried to parse non escape as escape \{show escape}" +``` + +Parse any non-quote character + +```idris +character : Eff Parser Char +character = oneOfM (the (List _) [escaped, normal]) +``` + +Run a parser until it runs out + +```idris +parseMany : Eff Parser t -> Eff Parser (List t) +parseMany x = do + Just next <- lift $ catch (\_ => pure Nothing) (map Just x) + | _ => pure [] + map (next ::) (parseMany x) +``` + +Parse any number of non-quote characters + +```idris +characters : Eff Parser (List Char) +characters = parseMany character +``` + +Parse a quote delimited string + +```idris +parseString : Eff Parser (List Char) +parseString = do + quote + xs <- characters + quote + pure xs +``` + +Run a parser + +```idris +runParser : Eff Parser t -> String -> Maybe t +runParser m str = + map fst . head' . rights + . extract . runChoose {f = List} . runExcept . runState (unpack str) + $ m + +``` + +## Part Functions + +### Part 1 + +```idris +part1 : Eff (PartEff String) (Nat, List (List Char, List Char)) +part1 = do + inputs <- map (lines . trim) $ askAt "input" + let outputs = map (map pack . runParser parseString) inputs + let pairs = zip inputs outputs + let debug_out = joinBy "\n" . map show $ pairs + putStrLn debug_out + ?part1_rhs_1 +``` + +