Hot answers tagged

7

The OP is doing this as exercise, but for anyone who found this question on google like I did and wants a fast solution: import Parsec's Token and Language. Then you can do this in GHCi: >>>let lexer = makeTokenParser haskellDef >>>let p = stringLiteral lexer >>>runParser p () "" "\"Hi\\n\"" Right "Hi\n" According to the Parsec ...


5

Whenever I find myself using a lambda, I usually replace it with a local definition: factor = unit >>= factor' where factor' x = try (char '?' >> return (Or Epsilon x)) <|> try (char '+' >> return (Then x (Star x))) <|> try (char '*' >> return (Star x)) <|> return x Since you only need to look at ...


3

for comparison Here are some other ways I've found to parse indentation sensitive language: Use Text.Parsec.Indent Here is a blog post containing an example: (link) Have a look at the language-python package. It's lexer is built using alex: (link) Purescript handles indentation via the mark, checkIndentation, indented and same functions: (link) Grep the ...


2

Parsers are for parsing This is probably the strangest primality test I've ever seen. So for those that want to write their own primality test, please don't use this approach. Almost all your functions can be written without a Parser. toXTerminatedAs is probably the best example: toXTerminatedAs :: Int -> String toXTerminatedAs n = replicate n 'a' ++ "...


2

One interesting thing you could do is create the higher level date parser parseDate using the alternative instance on Parser with: parseDate :: Parser Date parseDate = do date <- parseISODate <|> parseNiceDate <|> parseWeirdDate <|> parseWTFDate <|> parseWordyDate eof return date This really just makes it easy to read the ...


2

Where you used case you could have used pattern matching. This yields more idiomatic code in many cases. isNumerical :: Term -> Bool isNumerical TmZero = True isNumerical (TmSucc subterm) = isNumerical subterm isNumerical (TmPred subterm) = isNumerical subterm isNumerical _ = False eval :: Term -> Term eval TmTrue = TmTrue eval TmFalse = TmFalse ...


2

Let's start with your imports. You should get rid of those you don't need, e.g. System.IO, import Data.Either.Extra (fromRight). Next, you shouldn't mix integral and floating point calculations, unless you're fine with imprecise results. For example, integerLog will give the wrong answer around 1024: ghci> integerLog (2^1023) 1023 ghci> integerLog (2^...


2

do digits <- someParser return $ someFunction digits is just someFunction <$> someParser We can make parseNumberBase a lot shorter with that information: -- | Parses a number at a specific base parseNumberBase :: Char -> Parser LispVal parseNumberBase 'b' = Number . fromJust . readBinary <$> many1 (oneOf "01") parseNumberBase 'o' =...


1

Huh. I never got to solve that kata, so congratulations first. Working with a given AST As you said yourself, the AST type isn't optimal. We could use data AST = Imm Int | Arg Int | BinOp BinOp AST AST deriving (Eq, Show) data BinOp = Add | Mul | Sub | Div deriving (Eq, Show) instead and simplify both pass2 and pass3 a lot. ...


1

Instead of notFollowedBy, you can use noneOf in chunk: chunk :: Parser (Merge String) chunk = Chunk <$> many1 (noneOf "<") <|> try (sequence [char '<', noneOf "<"]) That prevents a lookahead but it is no longer that nice to look at. format can be rewritten without explicit recursion if we use concatMap: format :: HM.HashMap String ...


1

The use of stack tracers is somewhat unconventional in a standard parsec implementation, however this appears to be functional. I would recommend having a look at "implementing parsec compilers in Functional Scala" by Shenzun Wong, this should give you a more grounded understanding of the intricacies of the process. I would recommend replacing the use of ...


1

First of all, you can batch the skipping of braces (and return ()): unparsedBlock :: Parser () unparsedBlock = braces (skipMany (skipMany (noneOf "{}") <|> unparsedBlock)) Unfortunately, this is also probably quite inefficient, as this still works on a character-by-character basis, and uses stuff like manyAccum under the hood. If you really need ...


1

You could cut down on the repetitive elements with a local function binding. echar = let yield :: Char -> Char -> Parsec String () Char yield c d = char c >> return d in (char '\\' >> ( yield 'b' '\b' <|> yield 'f' '\f' -- ... Then perhaps cut out the repeated applications with a fold. import ...


1

I have figured out a better way of writing the code. Here I have 2 improvements: 1. Implementing notCommentEnd Reconsidering my first attempt, the problem is that notParser does not consume anything but many are expecting a parser that at least consumes one character so that it can terminate. If we make the assumption that the input string is non-empty ...


1

I've solved it by using Text.ParserCombinators.Parsec.GeneralizedToken from MissingH package: psOpBracket = tokeng (\x -> case x of TokOpBracket -> Just Nothing ; _ -> Nothing) I had to use tokenPrim for cases when Token constructor takes arguments, though.


Only top voted, non community-wiki answers of a minimum length are eligible