mirror of
https://github.com/Smaug123/WoofWare.PrattParser
synced 2025-10-06 01:48:39 +00:00
Split out into an example
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
namespace PrattParser
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
type Expr =
|
||||
| Plus of Expr * Expr
|
||||
| Times of Expr * Expr
|
||||
| UnaryMinus of Expr
|
||||
| Minus of Expr * Expr
|
||||
| Int of int
|
||||
| FunctionCall of Expr * Expr
|
||||
| Var of string
|
||||
| Factorial of Expr
|
||||
| Paren of Expr
|
||||
| IfThenElse of Expr * Expr * Expr
|
||||
| IfThen of Expr * Expr
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Expr =
|
||||
let plus a b = Expr.Plus (a, b)
|
||||
let times a b = Expr.Times (a, b)
|
||||
let unaryMinus a = Expr.UnaryMinus a
|
||||
let minus a b = Expr.Minus (a, b)
|
||||
let constInt a = Expr.Int a
|
||||
let functionCall f x = Expr.FunctionCall (f, x)
|
||||
let var name = Expr.Var name
|
||||
let factorial a = Expr.Factorial a
|
||||
let paren a = Expr.Paren a
|
||||
|
||||
let ifThenElse ifClause thenClause elseClause =
|
||||
Expr.IfThenElse (ifClause, thenClause, elseClause)
|
||||
|
||||
let ifThen ifClause thenClause = Expr.IfThen (ifClause, thenClause)
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
type TokenType =
|
||||
| Plus
|
||||
| Minus
|
||||
| Times
|
||||
| ConstInt
|
||||
| LeftBracket
|
||||
| RightBracket
|
||||
| Var
|
||||
| Factorial
|
||||
| If
|
||||
| Then
|
||||
| Else
|
||||
|
||||
type Token =
|
||||
{
|
||||
Type : TokenType
|
||||
/// The token is represented in the string as s.[left .. left + len], i.e. inclusive.
|
||||
Trivia : int * int
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Token =
|
||||
let standalone (ty : TokenType) (left : int) (len : int) =
|
||||
{
|
||||
Type = ty
|
||||
Trivia = (left, len)
|
||||
}
|
||||
|
||||
let standalone' (ty : TokenType) (singleCharPos : int) =
|
||||
{
|
||||
Type = ty
|
||||
Trivia = (singleCharPos, 1)
|
||||
}
|
||||
|
||||
let (|SingleChar|_|) (i : int, c : char) : Token option =
|
||||
match c with
|
||||
| '(' -> standalone' TokenType.LeftBracket i |> Some
|
||||
| ')' -> standalone' TokenType.RightBracket i |> Some
|
||||
| '*' -> standalone' TokenType.Times i |> Some
|
||||
| '+' -> standalone' TokenType.Plus i |> Some
|
||||
| '-' -> standalone' TokenType.Minus i |> Some
|
||||
| '!' -> standalone' TokenType.Factorial i |> Some
|
||||
| _ -> None
|
@@ -1,66 +0,0 @@
|
||||
namespace PrattParser
|
||||
|
||||
open System
|
||||
open System.Globalization
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Example =
|
||||
let private atom (inputString : string) (token : Token) : Expr option =
|
||||
match token.Type with
|
||||
| TokenType.ConstInt ->
|
||||
let start, len = token.Trivia
|
||||
|
||||
Int32.Parse (inputString.AsSpan().Slice (start, len), NumberStyles.None, CultureInfo.InvariantCulture)
|
||||
|> Expr.constInt
|
||||
|> Some
|
||||
| TokenType.Var ->
|
||||
let start, len = token.Trivia
|
||||
Some (Expr.var (inputString.Substring (start, len)))
|
||||
| TokenType.Plus
|
||||
| TokenType.Minus
|
||||
| TokenType.Times
|
||||
| TokenType.Factorial
|
||||
| TokenType.If
|
||||
| TokenType.Then
|
||||
| TokenType.Else
|
||||
| TokenType.LeftBracket
|
||||
| TokenType.RightBracket -> None
|
||||
|
||||
let parser =
|
||||
Parser.empty _.Type
|
||||
|> Parser.defineAtoms atom
|
||||
|> Parser.withUnaryPostfix TokenType.Factorial (7, ()) Expr.factorial
|
||||
|> Parser.withUnaryPrefix TokenType.Plus ((), 5) id
|
||||
|> Parser.withUnaryPrefix TokenType.Minus ((), 5) Expr.unaryMinus
|
||||
|> Parser.withInfix TokenType.Plus (1, 2) Expr.plus
|
||||
|> Parser.withInfix TokenType.Minus (1, 2) Expr.minus
|
||||
|> Parser.withInfix TokenType.Times (1, 2) Expr.times
|
||||
|> Parser.withBracketLike
|
||||
TokenType.LeftBracket
|
||||
{
|
||||
ConsumeAfterFinalToken = false
|
||||
BoundaryTokens = [ TokenType.RightBracket ]
|
||||
Construct = Seq.exactlyOne >> Expr.paren
|
||||
}
|
||||
|> Parser.withBracketLike
|
||||
TokenType.If
|
||||
{
|
||||
ConsumeAfterFinalToken = true
|
||||
BoundaryTokens = [ TokenType.Then ; TokenType.Else ]
|
||||
Construct =
|
||||
fun s ->
|
||||
match s with
|
||||
| [ ifClause ; thenClause ; elseClause ] -> Expr.ifThenElse ifClause thenClause elseClause
|
||||
| _ -> failwith "logic error"
|
||||
}
|
||||
|> Parser.withBracketLike
|
||||
TokenType.If
|
||||
{
|
||||
ConsumeAfterFinalToken = true
|
||||
BoundaryTokens = [ TokenType.Then ]
|
||||
Construct =
|
||||
fun s ->
|
||||
match s with
|
||||
| [ ifClause ; thenClause ] -> Expr.ifThen ifClause thenClause
|
||||
| _ -> failwith "logic error"
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
namespace PrattParser
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Lexer =
|
||||
let private (|Digit|_|) (c : char) : byte option =
|
||||
if '0' <= c && c <= '9' then
|
||||
Some (byte c - byte '0')
|
||||
else
|
||||
None
|
||||
|
||||
let lex (s : string) : Token seq =
|
||||
seq {
|
||||
let mutable i = 0
|
||||
|
||||
while i < s.Length do
|
||||
match i, s.[i] with
|
||||
| Token.SingleChar token ->
|
||||
i <- i + 1
|
||||
yield token
|
||||
| startI, Digit _ ->
|
||||
i <- i + 1
|
||||
let mutable shouldBreak = false
|
||||
|
||||
while i < s.Length && not shouldBreak do
|
||||
match s.[i] with
|
||||
| Digit _ -> i <- i + 1
|
||||
| _ -> shouldBreak <- true
|
||||
|
||||
yield Token.standalone TokenType.ConstInt startI (i - startI)
|
||||
| _, ' ' -> i <- i + 1
|
||||
| _, 'i' when i < s.Length - 1 && s.[i + 1] = 'f' ->
|
||||
yield Token.standalone TokenType.If i 2
|
||||
i <- i + 2
|
||||
| _, 't' when i < s.Length - 3 && s.[i + 1 .. i + 3] = "hen" ->
|
||||
yield Token.standalone TokenType.Then i 4
|
||||
i <- i + 4
|
||||
| _, 'e' when i < s.Length - 3 && s.[i + 1 .. i + 3] = "lse" ->
|
||||
yield Token.standalone TokenType.Else i 4
|
||||
i <- i + 4
|
||||
| startI, c ->
|
||||
if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') then
|
||||
i <- i + 1
|
||||
let mutable shouldBreak = false
|
||||
|
||||
while i < s.Length && not shouldBreak do
|
||||
let c = s.[i]
|
||||
|
||||
if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') then
|
||||
i <- i + 1
|
||||
else
|
||||
shouldBreak <- true
|
||||
|
||||
yield Token.standalone TokenType.Var startI (i - startI)
|
||||
else
|
||||
failwithf "Could not tokenize, char %c, at position %i" c startI
|
||||
}
|
@@ -252,4 +252,9 @@ module Parser =
|
||||
|
||||
go lhs rest
|
||||
|
||||
let parse parser inputString tokens = parseInner parser inputString tokens 0
|
||||
let parse<'tokenTag, 'token, 'expr when 'tokenTag : comparison>
|
||||
(parser : Parser<'tokenTag, 'token, 'expr>)
|
||||
(inputString : string)
|
||||
(tokens : 'token list)
|
||||
=
|
||||
parseInner parser inputString tokens 0
|
||||
|
@@ -6,10 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Domain.fs"/>
|
||||
<Compile Include="Lexer.fs"/>
|
||||
<Compile Include="Parser.fs"/>
|
||||
<Compile Include="Example.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user