How to resolve the algorithm ASCII art diagram converter step by step in the Haskell programming language
How to resolve the algorithm ASCII art diagram converter step by step in the Haskell programming language
Table of Contents
Problem Statement
Given the RFC 1035 message diagram from Section 4.1.1 (Header section format) as a string: http://www.ietf.org/rfc/rfc1035.txt Where (every column of the table is 1 bit): Write a function, member function, class or template that accepts a similar multi-line string as input to define a data structure or something else able to decode or store a header with that specified bit structure. If your language has macros, introspection, code generation, or powerful enough templates, then accept such string at compile-time to define the header data structure statically. Such "Header" function or template should accept a table with 8, 16, 32 or 64 columns, and any number of rows. For simplicity the only allowed symbols to define the table are + - | (plus, minus, pipe), and whitespace. Lines of the input string composed just of whitespace should be ignored. Leading and trailing whitespace in the input string should be ignored, as well as before and after each table row. The box for each bit of the diagram takes four chars "+--+". The code should perform a little of validation of the input string, but for brevity a full validation is not required. Bonus: perform a thoroughly validation of the input string.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm ASCII art diagram converter step by step in the Haskell programming language
The provided Haskell code offers a powerful set of functions for reading and parsing tabular data into a custom data structure. Let's break down the code step by step:
-
Data Structure for Tabular Data:
Field a
data type represents a single field in a row, with its name, size, and an optional value.Data a
data type represents a collection of rows, where each row is represented as a list ofField
s.
-
Parsing Tabular Data using ReadP:
- The
readP_to_S
function is used to parse tabular data using the ReadP library. - The
parseData
function defines the grammar for parsing tabular data. It checks the size of the table, parses rows, and verifies that row sizes are consistent.
- The
-
Parsing Row Header and Fields:
parseHeader
parses the header line and checks its size.parseRow
parses a single row, checking for consistency.parseField
parses an individual field, ensuring proper alignment.
-
Emulation of Bitstream Reading:
readData
function takes aData
structure and a stream of bits (represented as a list) and populates thefieldValue
field of eachField
with the corresponding bits.
-
A Sample Diagram of DNS Header:
- The
diagram
variable is a string that represents a diagram of a DNS header.
- The
-
A Sample Data:
dataSample
is a string containing sample data representing a DNS header in binary format.
-
Alternative Parsing Function:
readData
function provides an alternate way to parse tabular data. It expects input as a list of lines and checks for alignment and field sizes.readHLine
parses and checks the header line.readField
parses and checks individual fields.readRow
parses a single row, checking for consistency.
The code demonstrates how to parse tabular data into a structured format, handling alignment and consistency checks. It also includes an emulation function for reading a bitstream into the data structure and another function for parsing data from lines.
Source code in the haskell programming language
import Text.ParserCombinators.ReadP
import Control.Monad (guard)
data Field a = Field { fieldName :: String
, fieldSize :: Int
, fieldValue :: Maybe a}
instance Show a => Show (Field a) where
show (Field n s a) = case a of
Nothing -> n ++ "\t" ++ show s
Just x -> n ++ "\t" ++ show s ++ "\t" ++ show x
newtype Data a = Data { fields :: [Field a] }
instance Show a => Show (Data a) where
show (Data fs) = "NAME\tSIZE\tVALUE\n" ++ unlines (show <$> fs)
instance Read (Data a) where
readsPrec _ = readP_to_S parseData
parseData = do n <- parseHeader
guard (n `elem` [8,16,32,64]) -- check size of the table
Data . concat <$> many1 (parseRow n)
where
parseRow n = do
fs <- char '|' *> many parseField <* char '\n'
guard $ sum (fieldSize <$> fs) == n -- check that size of all fields match the row size
m <- parseHeader
guard $ m == n -- check that all rows have the same size
return fs
parseHeader = do
char '+'
n <- length <$> many1 (string "--+")
char '\n'
return n
parseField = do
s1 <- many (char ' ')
f <- munch1 $ not . (`elem` " |")
s2 <- many (char ' ')
char '|'
let n = (length (s1 ++ f ++ s2) + 1) `div` 3
return $ Field f n Nothing
-- emulation of reading a stream of bits
readData :: Data a -> [b] -> Data [b]
readData d = Data . go (fields d)
where
go fs [] = (\f -> f {fieldValue = Nothing}) <$> fs
go (f:fs) s =
let (x, xs) = splitAt (fieldSize f) s
in f {fieldValue = Just x} : go fs xs
diagram = unlines
["+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"| ID |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"|QR| Opcode |AA|TC|RD|RA| Z | RCODE |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"| QDCOUNT |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"| ANCOUNT |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"| NSCOUNT |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"
,"| ARCOUNT |"
,"+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+"]
dataSample = concat
["011110000100011101111011101111110101010010010110",
"111000010010111000011011111100010110100110100100"]
import Data.List (nub)
import Data.List.Split (splitOn)
import Control.Monad (unless)
readData :: String -> Either String (Data a)
readData d = process $ lines d
where
process d = do
let l = length (head d)
unless (all ((l ==) . length) d) $ Left "Table is not aligned!"
w <- readHLine (head d)
let rows = filter ((/= "+-") . nub) d
Data . concat <$> traverse (readRow w) rows
readHLine s = do
let cols = splitOn "--" s
unless (nub cols == ["+"]) $ Left ("Invalid header: " ++ s)
return $ length cols - 1
readField s = do
let n = length s + 1
unless (n `mod` 3 == 0) $ Left ("Field is not aligned: " ++ s)
return $ Field (filter (/= ' ') s) (n `div` 3) Nothing
readRow n s = do
let fields = filter (not.null) $ splitOn "|" s
row <- traverse readField fields
unless (sum (fieldSize <$> row) == n) $ Left $ "Fields are not aligned at row\n " ++ s
return row
You may also check:How to resolve the algorithm Cartesian product of two or more lists step by step in the Fōrmulæ programming language
You may also check:How to resolve the algorithm Pangram checker step by step in the J programming language
You may also check:How to resolve the algorithm Calendar - for REAL programmers step by step in the XLISP programming language
You may also check:How to resolve the algorithm Feigenbaum constant calculation step by step in the Ada programming language
You may also check:How to resolve the algorithm Nth root step by step in the Delphi programming language