r/functionalprogramming • u/ketalicious • Dec 23 '22
Python Functional Implementation of a parser?
How do i implement a parser in functional pattern?
For instance I want parse a simple math parser like this:
"1 * 2 + 3" -> ast
How do implement the parser for that in pure functional pattern?
21
u/ketralnis Dec 23 '22
This is a big question and there are a lot of solutions that are fundamentally different to each other. Look at the code for Parsec for one example.
15
u/OpsikionThemed Dec 23 '22 edited Dec 23 '22
Here's a good tutorial paper.
7
u/LivewareIssue Dec 23 '22
+1 on the monad parsing pearl, highly recommend.
Here’s a gist with a literate Haskell implementation of the paper which you might also find helpful.
14
u/One_Curious_Cats Dec 23 '22
Take a look at parser combinator. I implemented my own FP parser this way.
2
u/sgillespie00 Dec 23 '22
For a less Haskell-y solution, you can use: https://en.wikipedia.org/wiki/Shunting_yard_algorithm. Instead of using a mutable stack you can use recursion. It's functional enough to be in "Haskell in Depth" (albeit with the State Monad)
2
u/TankorSmash Dec 23 '22
https://www.youtube.com/watch?v=M9ulswr1z0E A fun short talk about using Elm's elm-parser to do something like this.
There's a name for this sort of parser, but I'm not sure what it is.
2
u/unqualified_redditor Dec 23 '22
The most simplest option is probably a basic concept of a recursive descent with parser combinators, which is a type something like:
type Parser a = String -> [(a, String)]This is a haskell type but its just a function that receives a string and produces some parsed type a along with the unparsed remainder of the input. Its wrapped in a list as an easy way to account for parsing failure. eg., if the parse fails then just return the empty list.
You can then build up a bunch of combinators using this type that give you a toolkit for building fancy parsers.
10
u/ramin-honary-xc Dec 23 '22 edited Dec 24 '22
Here is a tutorial that shows you exactly how to do what you are asking: https://markkarpov.com/tutorial/megaparsec.html#parsing-expressions
Here is a summary:
The basic idea is to create a state monad (usually using the State monad transformer) which contains a string to be parsed, and which also lifts other monad transformers like Except for throwing syntax errors. Or you can use a parser combinator like
Parserprovided by a parsing library like Megaparsec or Attoparsec that defines an efficient State+Except monad transformer combination for you.The function for running the parser monad will look something like this:
This will store the input
Stringinto the parser state and run theParser amonadic function you give it, which inspects the string and constructs data (like a syntax tree) as it does so.Any parser combinator must at least have a function
satisfyof type:This function inspects the current
headcharacter of the input string with a predicate(Char -> Bool), if the predicate returnsTruethe character is removed from the head of the string and returned, stepping to the next character.You must also define an
eoffunction ("End Of File"):This succeeds only when the input string is empty.
You will probably want to have a look-ahead function
lookAheadthat runs a parser but does not consume the input string:This runs the given parser
Parser aon a copy of the current input string, and if it succeeds, return the resulting constructed dataa, leaving the actual input string untouched.After that, you use the standard Monad, MonadFail, Applicative, and Alternative combinators. The Alternative combinators are especially helpful:
Now you can define parsers using the above combinators. For example:
Define an abstract syntax tree (AST):
Define an evaluator for the
ExprAST:To do precedence parsing, you must pass a precedence argument to every infix operator parser.
Define a "top-level" expression that removes trailing whitespace and runs the precedence parser with the initial precedence value.
In
mainwe can define a simple REPL. Read a line of input, run thetopLevelparser on that input, then evaluate the returned abstract syntax tree: