mirror of
https://github.com/Smaug123/WoofWare.PrattParser
synced 2025-10-05 09:28:41 +00:00
Split out into an example
This commit is contained in:
77
PrattParser.Example/Domain.fs
Normal file
77
PrattParser.Example/Domain.fs
Normal file
@@ -0,0 +1,77 @@
|
||||
namespace ParseExample
|
||||
|
||||
[<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
|
67
PrattParser.Example/Example.fs
Normal file
67
PrattParser.Example/Example.fs
Normal file
@@ -0,0 +1,67 @@
|
||||
namespace ParseExample
|
||||
|
||||
open System
|
||||
open System.Globalization
|
||||
open PrattParser
|
||||
|
||||
[<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"
|
||||
}
|
56
PrattParser.Example/Lexer.fs
Normal file
56
PrattParser.Example/Lexer.fs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace ParseExample
|
||||
|
||||
[<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
|
||||
}
|
18
PrattParser.Example/PrattParser.Example.fsproj
Normal file
18
PrattParser.Example/PrattParser.Example.fsproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Domain.fs" />
|
||||
<Compile Include="Lexer.fs" />
|
||||
<Compile Include="Example.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PrattParser\PrattParser.fsproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Reference in New Issue
Block a user