diff --git a/tests/LexAndYaccCalculator/LexAndYaccCalculator.fsproj b/tests/LexAndYaccCalculator/LexAndYaccCalculator.fsproj new file mode 100644 index 0000000..e5d9a0e --- /dev/null +++ b/tests/LexAndYaccCalculator/LexAndYaccCalculator.fsproj @@ -0,0 +1,27 @@ + + + + Exe + net6.0 + lexandyacccalculator_example + + + + + + --module Parser + + + --module Lexer --unicode + + + + + + + + + + + + diff --git a/tests/LexAndYaccCalculator/Lexer.fsl b/tests/LexAndYaccCalculator/Lexer.fsl new file mode 100644 index 0000000..545237a --- /dev/null +++ b/tests/LexAndYaccCalculator/Lexer.fsl @@ -0,0 +1,48 @@ +{ + +open FSharp.Text.Lexing +open Parser + +type Lexbuf = LexBuffer + +let lexeme (lexbuf : Lexbuf) = Lexbuf.LexemeString lexbuf + +let newline (lexbuf: LexBuffer<_>) = + lexbuf.StartPos <- lexbuf.StartPos.NextLine + +} + +// Regular expressions +let whitespace = [' ' '\t' ] +let newline = ('\n' | '\r' '\n') +let separator = '_' +let letter = '\Lu' | '\Ll' | '\Lt' | '\Lm' | '\Lo' | '\Nl' +let digit = '\Nd' +let anystring = anychar* +let integer = digit ((digit | separator)* digit)? +let char = '\'' ( [^'\\''\n''\r''\t''\b'] | escape_char) '\'' + +let ident_char = + letter + | digit + | '_' +let ident = letter ident_char* + +let anychar = [^'\n''\r'] + +let comment = "/*" anystring "*/" + +rule tokenstream = parse +| whitespace { tokenstream lexbuf } +| newline { newline lexbuf; tokenstream lexbuf } +// Identifiers +| ident { ID(lexeme lexbuf) } +| integer { INT(int (lexeme lexbuf)) } +// Operators +| "+" { OP_PLUS } +| "-" { OP_MINUS } +| "*" { OP_TIMES } +| "/" { OP_DIVIDE } +// -------------------------- +| _ { failwith ("ParseError" + LexBuffer<_>.LexemeString lexbuf) } +| eof { Parser.EOF } diff --git a/tests/LexAndYaccCalculator/Parser.fsi b/tests/LexAndYaccCalculator/Parser.fsi new file mode 100644 index 0000000..5b22e20 --- /dev/null +++ b/tests/LexAndYaccCalculator/Parser.fsi @@ -0,0 +1,40 @@ +// Signature file for parser generated by fsyacc +module Parser +type token = + | OP_PLUS + | OP_MINUS + | OP_TIMES + | OP_DIVIDE + | LPAREN + | RPAREN + | EOF + | INT of (int) + | ID of (string) +type tokenId = + | TOKEN_OP_PLUS + | TOKEN_OP_MINUS + | TOKEN_OP_TIMES + | TOKEN_OP_DIVIDE + | TOKEN_LPAREN + | TOKEN_RPAREN + | TOKEN_EOF + | TOKEN_INT + | TOKEN_ID + | TOKEN_end_of_input + | TOKEN_error +type nonTerminalId = + | NONTERM__startstart + | NONTERM_start + | NONTERM_Expr +/// This function maps tokens to integer indexes +val tagOfToken: token -> int + +/// This function maps integer indexes to symbolic token ids +val tokenTagToTokenId: int -> tokenId + +/// This function maps production indexes returned in syntax errors to strings representing the non terminal that would be produced by that production +val prodIdxToNonTerminal: int -> nonTerminalId + +/// This function gets the name of a token as a string +val token_to_string: token -> string +val start : (FSharp.Text.Lexing.LexBuffer<'cty> -> token) -> FSharp.Text.Lexing.LexBuffer<'cty> -> ( Syntax.Expr ) diff --git a/tests/LexAndYaccCalculator/Parser.fsy b/tests/LexAndYaccCalculator/Parser.fsy new file mode 100644 index 0000000..7d7b9ea --- /dev/null +++ b/tests/LexAndYaccCalculator/Parser.fsy @@ -0,0 +1,27 @@ +%{ +open Syntax +%} + +%start start +%token ID +%token INT + +%token LPAREN RPAREN EOF + +%token OP_PLUS OP_MINUS OP_TIMES OP_DIVIDE + +%left OP_PLUS OP_MINUS +%left OP_TIMES OP_DIVIDE +%type < Syntax.Expr > start + + +%% + +start: Expr { $1 } + +Expr: ID { Var($1) } + | INT { Int($1) } + | Expr OP_PLUS Expr { Add($1,$3) } + | Expr OP_MINUS Expr { Sub($1,$3) } + | Expr OP_TIMES Expr { Mul($1,$3) } + | Expr OP_DIVIDE Expr { Div($1,$3) } diff --git a/tests/LexAndYaccCalculator/Program.fs b/tests/LexAndYaccCalculator/Program.fs new file mode 100644 index 0000000..8d11699 --- /dev/null +++ b/tests/LexAndYaccCalculator/Program.fs @@ -0,0 +1,20 @@ +open System.IO +open FSharp.Text.Lexing +open Syntax + +let testLexerAndParserFromString text expected = + let lexbuf = LexBuffer.FromString text + + let parse = Parser.start Lexer.tokenstream lexbuf + + printfn "parse: result = %A, expected %A" parse expected + +testLexerAndParserFromString "1" (Int 1) +testLexerAndParserFromString "hello" (Var "hello") +testLexerAndParserFromString "1 + 1" (Add((Int 1),(Int 1))) +testLexerAndParserFromString "1 - 1" (Sub((Int 1),(Int 1))) +testLexerAndParserFromString "1 * 1" (Mul((Int 1),(Int 1))) +testLexerAndParserFromString "1 / 1" (Div((Int 1),(Int 1))) +testLexerAndParserFromString "1 + 2 + 3" (Add(Add(Int 1, Int 2), Int 3)) +testLexerAndParserFromString "1 + 2 * 3" (Add(Int 1, Mul(Int 2, Int 3))) +testLexerAndParserFromString "1 * 2 + 3" (Add(Mul(Int 1, Int 2), Int 3)) diff --git a/tests/LexAndYaccCalculator/Syntax.fs b/tests/LexAndYaccCalculator/Syntax.fs new file mode 100644 index 0000000..a89e860 --- /dev/null +++ b/tests/LexAndYaccCalculator/Syntax.fs @@ -0,0 +1,11 @@ +module Syntax + +type Id = Id of string + +type Expr = + | Var of string + | Int of int + | Add of Expr * Expr + | Sub of Expr * Expr + | Mul of Expr * Expr + | Div of Expr * Expr