Skip to content

Instantly share code, notes, and snippets.

@m0rphed
Last active November 25, 2021 18:22
Show Gist options
  • Save m0rphed/6e52834c21fd45674aaaa28f3f207b9f to your computer and use it in GitHub Desktop.
Save m0rphed/6e52834c21fd45674aaaa28f3f207b9f to your computer and use it in GitHub Desktop.
FParsec dilemma
#r "nuget: FParsec"
open System.Numerics
module AST =
type Name = string
type Expr =
| VarCall of Name
| Literal of PrimitiveT // Unit, Bool, Int, ...
| Lambda of string * Expr
| LetFunc of Name * Expr * Expr
| LetRec of Name * Expr * Expr
| Apply of Expr * Expr
| IfElse of Expr * Expr * Expr
| BinaryOp of (Expr * BinOp * Expr)
| UnaryOp of (UnOp * Expr)
and PrimitiveT =
| UnitT
| Str of string
| Bool of bool
| Num of NumericT
and NumericT =
| Int of int // 1
| Float of float // 1.5
| BigInt of BigInteger // 1I
and BinOp =
{ Symbol: Name
OpType: PrimitiveT
Args: Expr * Expr }
and UnOp =
{ Symbol: Name
OpType: PrimitiveT
Arg: Expr }
/// Error should be thrown
/// when parsing fails
exception MiniMLParsingError of string
/// Error should be thrown
/// when running of expression or statement fails
exception MiniMLRuntimeError of string
/// Extends functionality of BigInteger from System.Numerics
module BigIntegerExt =
let parse (str: string) =
match BigInteger.TryParse(str) with
| true, parsed -> parsed
| false, rem ->
$"Error parsing: {str}, expected number literal (BigInt), collected: {rem};"
|> MiniMLParsingError
|> raise
let equal (x: BigInteger) (y: BigInteger) = x = y
let notEqual x y = not (equal x y)
// Aliases for math operators
let add x y = BigInteger.Add(x, y)
let subtract x y = BigInteger.Subtract(x, y)
let modulus x y = BigInteger.Remainder(x, y)
let multiply x y = BigInteger.Multiply(x, y)
let pow x (exp: BigInteger) =
match System.Int32.TryParse(string exp) with
| true, num -> BigInteger.Pow(x, num)
| false, _rem ->
$"Error while trying to raise {x} to power of {exp} - specified exponent is too large"
|> MiniMLRuntimeError
|> raise
let divide x y = BigInteger.Divide(x, y)
open FParsec
open AST
module Parser =
/// Parser for BigInt label 'I' e.g. "123I", "0I", "-42I"
let bigIntLabel = pchar 'I'
/// Returns parser which matches passed string,
/// also consumes 0+ spaces after matched string
let pWord str = pstring str .>> spaces
/// Requiring that passed parser
/// be wrapped in parentheses:
/// '(' + <passedPrs something> + ')'
let parens passedPrs =
passedPrs
|> between (pWord "(") (pWord ")")
/// Parses UnitT literal (Unit Type - repr. void in C#, unit in F#)
let unitLiteral: Parser<_, Unit> = pWord "()"
/// Parses Str literal (System.String)
let strLiteral: Parser<PrimitiveT, Unit> =
// This line returns a list of chars, which we have to
// turn into a string before turning into a Str Value
pchar '\"' >>. manyCharsTill anyChar (pchar '\"')
|>> string |>> Str
// Discard the spaces at the end
.>> spaces
/// Parses Bool literal (System.Boolean)
let boolLiteral: Parser<PrimitiveT, Unit> =
(pWord "true" <|> pWord "false")
|>> function
| "true" -> Bool true
| "false" -> Bool false
| unexpected ->
"Expected 'true' or 'false' Bool literal; instead got: "
+ unexpected
|> MiniMLParsingError
|> raise
/// Parses Int literal (System.Int32)
let intNum: Parser<NumericT, Unit> = pint32 |>> int |>> Int
/// Parses Float literal (System.Double)
let floatNum: Parser<NumericT, Unit> = pfloat |>> Float
/// Parses BigInt literal (BigInteger from System.Numerics)
let bigIntNum: Parser<NumericT, Unit> =
many1CharsTill (satisfy isDigit) bigIntLabel
|>> BigIntegerExt.parse
|>> BigInt
/// Parses different number literals (e.g. System.Int32, System.Double)
let numberLiteral: Parser<NumericT, Unit> =
floatNum <|> bigIntNum <|> intNum
open Parser
let stringToParse = "12233I "
// run parser on input
let runP () =
match run numberLiteral stringToParse with
| Failure (msg, _, _) as err ->
failwith $"failure {msg}\n\tError => {err}"
| Success (result, _, _) -> result
let res = runP ()
res |> printfn "%A"
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment