├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitignore ├── README.md ├── example ├── add-and-store.waxt ├── noop.waxt └── return-i32.waxt ├── paket.dependencies ├── paket.lock ├── src ├── Waxt.Cli │ ├── Program.fs │ ├── Waxt.Cli.fsproj │ └── paket.references ├── Waxt.CodeGen │ ├── Library.fs │ ├── Waxt.CodeGen.fsproj │ └── paket.references ├── Waxt.Compiler │ ├── Library.fs │ ├── Waxt.Compiler.fsproj │ └── paket.references ├── Waxt.Lexer │ ├── Library.fs │ ├── Waxt.Lexer.fsproj │ └── paket.references ├── Waxt.Location │ ├── Locatable.fs │ ├── Pos.fs │ ├── Range.fs │ ├── Waxt.Location.fsproj │ └── paket.references ├── Waxt.Parser │ ├── Parse.fs │ ├── ParseExpr.fs │ ├── ParseFunc.fs │ ├── ParseFuncParams.fs │ ├── ParseSExpr.fs │ ├── ParseType.fs │ ├── ParseUntypedAst.fs │ ├── SExpr.fs │ ├── Waxt.Parser.fsproj │ └── paket.references ├── Waxt.Token │ ├── Library.fs │ ├── Waxt.Token.fsproj │ └── paket.references ├── Waxt.Type │ ├── Type.fs │ ├── TypeEnv.fs │ ├── Waxt.Type.fsproj │ └── paket.references ├── Waxt.TypeChecker │ ├── Error.fs │ ├── Expect.fs │ ├── Library.fs │ ├── SeqExt.fs │ ├── TypeExpr.fs │ ├── TypeFuncBodies.fs │ ├── TypeFuncSigs.fs │ ├── Waxt.TypeChecker.fsproj │ └── paket.references ├── Waxt.TypedAst │ ├── IndexedMap.fs │ ├── Library.fs │ ├── Waxt.TypedAst.fsproj │ └── paket.references ├── Waxt.UntypedAst │ ├── Expr.fs │ ├── FuncDef.fs │ ├── FuncName.fs │ ├── Stmt.fs │ ├── Waxt.UntypedAst.fsproj │ └── paket.references └── Waxt.Wasm │ ├── CodeSection.fs │ ├── FunctionSection.fs │ ├── Leb128.fs │ ├── Library.fs │ ├── MemorySection.fs │ ├── Section.fs │ ├── Serializable.fs │ ├── Type.fs │ ├── TypeIndex.fs │ ├── TypeSection.fs │ ├── Vector.fs │ ├── Waxt.Wasm.fsproj │ └── paket.references ├── test.sh ├── test ├── Waxt.Lexer.Test │ ├── Program.fs │ ├── Program.lex.verified.txt │ ├── Waxt.Lexer.Test.fsproj │ └── paket.references ├── Waxt.Location.Test │ ├── Program.fs │ ├── Waxt.Location.Test.fsproj │ └── paket.references ├── Waxt.Parser.Test │ ├── Program.fs │ ├── Program.returnI32.verified.txt │ ├── Program.returnI32SExpr.verified.txt │ ├── Program.voidFunc.verified.txt │ ├── Program.voidFuncSExpr.verified.txt │ ├── Waxt.Parser.Test.fsproj │ └── paket.references └── Waxt.TypeChecker.Test │ ├── IndexedMap.fs │ ├── Program.fs │ ├── Program.i32Const.verified.txt │ ├── Program.undefinedVar.verified.txt │ ├── Program.var.verified.txt │ ├── Waxt.TypeChecker.Test.fsproj │ └── paket.references └── waxt.sln /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fantomas-tool": { 6 | "version": "4.7.9", 7 | "commands": [ 8 | "fantomas" 9 | ] 10 | }, 11 | "paket": { 12 | "version": "7.1.5", 13 | "commands": [ 14 | "paket" 15 | ] 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.fsproj] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | .paket/ 4 | paket-files/ 5 | /*.wasm 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WAXT - WebAssembly eXtended Text Format 2 | 3 | WAT (WebAssembly Text Format) を少し拡張して、人間にとって書きやすく malloc / free や GC の実装で役立つことを目指した中間レベルの言語です。 4 | 5 | WAT と同じく S 式で記述し、コンパイラを用いて WASM (バイナリ形式) に変換したうえで、各種 WASM ランタイムで実行できます。 6 | 7 | ## 開発状況 8 | 9 | ``i32.add``, `i32.mul`, ``i32.store`` を用いた単純な関数定義をコンパイルできるようにしました。 10 | 11 | これから `let`, `set!` 特殊形式と型推論を実装しようとしています。 12 | 13 | ## 実装したい言語機能 14 | 15 | ### トップレベルでの関数定義 16 | 17 | WAXT : 18 | 19 | ```wasm 20 | (func add-and-store ([addr : i32] [x : i32] [y : i32]) 21 | (i32.store addr (+ x y))) 22 | ``` 23 | 24 | WAT (コンパイル後) : 25 | 26 | ```wasm 27 | (module 28 | (memory 1) 29 | (func $add-and-store (export "add-and-store") 30 | (param $addr i32) (param $x i32) (param $y i32) 31 | (i32.store 32 | (local.get $addr) 33 | (i32.add (local.get $x) (local.get $y))))) 34 | ``` 35 | 36 | ### 定数 37 | 38 | デフォルトですべての引数・束縛はイミュータブルであり、再代入はコンパイルエラーとなります。 39 | 40 | ```wasm 41 | (func foo i32 ([x : i32] [y : i32]) 42 | (let [x' (+ x 2) y' (+ y 3)] 43 | (* x' y'))) 44 | ``` 45 | 46 | ### 変数 47 | 48 | `$` プレフィックス付きの引数・束縛はミュータブルとなり、 `set!` 特殊形式による再代入が許可されます。 49 | 50 | ```wasm 51 | (func bar i32 ([$x : i32] [$y : i32]) 52 | (set! $x (+ $x 2)) 53 | (set! $y (+ $y 3)) 54 | (* $x $y)) 55 | ``` 56 | 57 | ## コード例の実行方法 58 | 59 | `example` ディレクトリ内にコード例があり、それらは以下のコマンドでコンパイルできます。 60 | 61 | ```bash 62 | dotnet run --project src/Waxt.Cli -- example/noop.waxt 63 | 64 | dotnet run --project src/Waxt.Cli -- example/return-i32.waxt 65 | 66 | dotnet run --project src/Waxt.Cli -- example/add-and-store.waxt 67 | ``` 68 | 69 | ## テストの実行方法 70 | 71 | ```bash 72 | ./test.sh 73 | ``` 74 | 75 | ## プロジェクトの依存関係 76 | 77 | ```mermaid 78 | graph TB 79 | Token-->Location 80 | Lexer-->Location 81 | Lexer-->Token 82 | UntypedAst-->Location 83 | UntypedAst-->Type 84 | Parser-->Location 85 | Parser-->Token 86 | Parser-->UntypedAst 87 | TypedAst-->Location 88 | TypedAst-->Type 89 | TypeChecker-->Location 90 | TypeChecker-->Type 91 | TypeChecker-->TypedAst 92 | TypeChecker-->UntypedAst 93 | CodeGen-->TypedAst 94 | CodeGen-->Wasm 95 | Compiler-->Lexer 96 | Compiler-->Location 97 | Compiler-->Parser 98 | Compiler-->TypeChecker 99 | Compiler-->TypedAst 100 | Compiler-->CodeGen 101 | Cli-->Compiler 102 | ``` 103 | -------------------------------------------------------------------------------- /example/add-and-store.waxt: -------------------------------------------------------------------------------- 1 | (func add-and-store ([addr : i32] [x : i32] [y : i32]) 2 | (i32.store addr (i32.add x y))) 3 | -------------------------------------------------------------------------------- /example/noop.waxt: -------------------------------------------------------------------------------- 1 | (func noop1 unit ()) 2 | 3 | (func noop2 ()) 4 | -------------------------------------------------------------------------------- /example/return-i32.waxt: -------------------------------------------------------------------------------- 1 | (func foo i32 ([x : i32] [y : i32]) 2 | (i32.mul (i32.add x 3) y)) 3 | 4 | (func bar i32 ([x : i32]) 5 | (i32.add x 4)) 6 | 7 | (func baz i32 ([x : i32] [y : i32]) 8 | (i32.add x (i32.mul y 5))) 9 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://api.nuget.org/v3/index.json 2 | 3 | storage: none 4 | framework: net6.0 5 | 6 | nuget Expecto ~> 9.0.4 7 | nuget FSharp.Core ~> 6.0.5 8 | nuget FsToolkit.ErrorHandling ~> 2.13.0 9 | nuget Thoth.Json.Net ~> 8.0.0 10 | nuget Verify ~> 17.9.0 11 | nuget Verify.Expecto ~> 17.9.0 12 | -------------------------------------------------------------------------------- /paket.lock: -------------------------------------------------------------------------------- 1 | STORAGE: NONE 2 | RESTRICTION: == net6.0 3 | NUGET 4 | remote: https://api.nuget.org/v3/index.json 5 | DiffEngine (10.0) 6 | EmptyFiles (>= 2.8) 7 | System.Management (>= 5.0) 8 | EmptyFiles (2.8) 9 | Expecto (9.0.4) 10 | FSharp.Core (>= 4.6) 11 | Mono.Cecil (>= 0.11.3) 12 | Fable.Core (3.7.1) 13 | FSharp.Core (6.0.5) 14 | FsToolkit.ErrorHandling (2.13) 15 | FSharp.Core (>= 4.7.2) 16 | Mono.Cecil (0.11.4) 17 | Newtonsoft.Json (13.0.1) 18 | SimpleInfoName (1.1.1) 19 | System.CodeDom (6.0) 20 | System.Management (6.0) 21 | System.CodeDom (>= 6.0) 22 | Thoth.Json.Net (8.0) 23 | Fable.Core (>= 3.0 < 4.0) 24 | FSharp.Core (>= 4.7.2) 25 | Newtonsoft.Json (>= 11.0.2) 26 | Verify (17.9) 27 | DiffEngine (>= 10.0) 28 | EmptyFiles (>= 2.8) 29 | Newtonsoft.Json (>= 13.0.1) 30 | SimpleInfoName (>= 1.1.1) 31 | Verify.Expecto (17.9) 32 | EmptyFiles (>= 2.8) 33 | Expecto (>= 9.0.4) 34 | FSharp.Core (>= 6.0.5) 35 | Verify (>= 17.9) 36 | -------------------------------------------------------------------------------- /src/Waxt.Cli/Program.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Cli 2 | 3 | open System 4 | open System.IO 5 | open Waxt.Compiler 6 | 7 | let error (msg: string) = 8 | Console.ForegroundColor <- ConsoleColor.Red 9 | Console.Error.WriteLine msg 10 | Console.ResetColor() 11 | 12 | [] 13 | let main args = 14 | if Array.isEmpty args then 15 | eprintfn "Usage: waxt " 16 | error "No input files" 17 | 1 18 | else 19 | let inputFilePath = args.[0] 20 | 21 | let src = 22 | try 23 | File.ReadAllText inputFilePath 24 | with 25 | | :? FileNotFoundException -> 26 | error "The specified file doesn't exist" 27 | exit 1 28 | 29 | | :? DirectoryNotFoundException -> 30 | error "The specified directory doesn't exist" 31 | exit 1 32 | 33 | | :? IOException -> 34 | error "Could not read the specified file" 35 | exit 1 36 | 37 | match compile src with 38 | | Ok bytes -> 39 | use file = File.Open("out.wasm", FileMode.Create) 40 | use writer = new BinaryWriter(file) 41 | writer.Write(Array.ofList bytes) 42 | 0 43 | 44 | | Error compileErrors -> 45 | compileErrors 46 | |> List.iter (fun err -> error (CompileError.toString err)) 47 | 48 | 1 49 | -------------------------------------------------------------------------------- /src/Waxt.Cli/Waxt.Cli.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Waxt.Cli/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /src/Waxt.CodeGen/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.CodeGen.Library 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.Location 6 | open Waxt.Wasm 7 | 8 | module WaxtType = Waxt.Type.Type 9 | 10 | module TypedAst = Waxt.TypedAst.Library 11 | 12 | type CodeGenError = CodeGenError of msg: string * at: Range 13 | 14 | module CodeGenError = 15 | let toString (CodeGenError (msg, at)) = 16 | let at = Range.toString at 17 | $"(%s{at}) %s{msg}" 18 | 19 | let private waxtTypeToWasmResultType (waxtType: WaxtType.Type) : Vector = 20 | match waxtType with 21 | | WaxtType.I32 -> [ I32 ] 22 | | WaxtType.I64 -> [ I64 ] 23 | | WaxtType.Unit -> [] 24 | |> Vec 25 | 26 | let private funcSigToFuncType (TypedAst.FuncSig (parameters, resultTy, _)) : Result> = 27 | result { 28 | let! parameters = 29 | parameters 30 | |> List.ofSeq 31 | |> List.map (fun (_, (_, (ty, at))) -> 32 | match ty with 33 | | WaxtType.I32 -> Ok I32 34 | | WaxtType.I64 -> Ok I64 35 | | WaxtType.Unit -> Error(CodeGenError("unit cannot be used as a parameter type", at))) 36 | |> List.sequenceResultA 37 | 38 | let result = waxtTypeToWasmResultType resultTy 39 | return FunctionType(Vec parameters, result) 40 | } 41 | 42 | let rec private genInsts (typedExpr: TypedAst.TypedExpr) : list = 43 | match typedExpr with 44 | | TypedAst.I32Const (n, _) -> [ I32Const n ] 45 | 46 | | TypedAst.I32Add (lhs, rhs, _) -> 47 | let lhs = genInsts lhs 48 | let rhs = genInsts rhs 49 | lhs @ rhs @ [ I32Add ] 50 | 51 | | TypedAst.I32Mul (lhs, rhs, _) -> 52 | let lhs = genInsts lhs 53 | let rhs = genInsts rhs 54 | lhs @ rhs @ [ I32Mul ] 55 | 56 | | TypedAst.Var (index, _, _) -> [ LocalGet(uint32 index) ] 57 | 58 | | TypedAst.I32Store (addr, content, _) -> 59 | let addr = genInsts addr 60 | let content = genInsts content 61 | addr @ content @ [ I32Store ] 62 | 63 | type FuncTyPool = list 64 | 65 | type CompiledFunc = TypeIndex * Code 66 | 67 | type State = FuncTyPool * list 68 | 69 | let genCode (funcs: TypedAst.TypedFuncs) : Result> = 70 | result { 71 | let! (funcTyPool, compiledFuncs) = 72 | (Ok([], []: State), funcs) 73 | ||> Seq.fold (fun state (_, (funcSig, funcBody)) -> 74 | result { 75 | let! (funcTyPool, compiledFuncs) = state 76 | let! funcType = funcSigToFuncType funcSig 77 | 78 | let (funcTyPool, index) = 79 | List.tryFindIndex ((=) funcType) funcTyPool 80 | |> Option.map (fun index -> (funcTyPool, index)) 81 | |> Option.defaultWith (fun () -> (funcTyPool @ [ funcType ], List.length funcTyPool)) 82 | 83 | let insts = funcBody |> List.map genInsts |> List.concat 84 | 85 | let compiledFunc = (TypeIndex(uint32 index), Code(Func(Vec [], Expr insts))) 86 | return (funcTyPool, compiledFunc :: compiledFuncs) 87 | }) 88 | 89 | let compiledFuncs = List.rev compiledFuncs 90 | 91 | let typeSection = TypeSection(Vec funcTyPool) 92 | 93 | let functionSection = FunctionSection(Vec(compiledFuncs |> List.map fst)) 94 | 95 | let codeSection = CodeSection(Vec(compiledFuncs |> List.map snd)) 96 | 97 | return Wasm(typeSection, functionSection, codeSection) 98 | } 99 | -------------------------------------------------------------------------------- /src/Waxt.CodeGen/Waxt.CodeGen.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Waxt.CodeGen 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Waxt.CodeGen/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /src/Waxt.Compiler/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Compiler.Library 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.CodeGen 6 | open Waxt.Lexer 7 | open Waxt.Location 8 | open Waxt.Parser 9 | open Waxt.TypeChecker 10 | open Waxt.UntypedAst 11 | open Waxt.Wasm 12 | 13 | type CompileError = 14 | private 15 | | FromLexer of LexError 16 | | FromParser of ParseError 17 | | FromTypeChecker of TypeError 18 | | FromCodeGen of CodeGenError 19 | 20 | module CompileError = 21 | let toString = 22 | function 23 | | FromLexer lexError -> LexError.toString lexError 24 | | FromParser parseError -> ParseError.toString parseError 25 | | FromTypeChecker typeError -> TypeError.toString typeError 26 | | FromCodeGen codeGenError -> CodeGenError.toString codeGenError 27 | 28 | let compile src : Result, list> = 29 | result { 30 | let! tokens = 31 | lex src 32 | |> Result.mapError (fun lexerError -> [ FromLexer lexerError ]) 33 | 34 | let! (sExprs, rest) = 35 | ParseSExpr.parseManySExpr Pos.origin tokens 36 | |> Result.mapError (fun parseError -> [ FromParser parseError ]) 37 | 38 | match rest with 39 | | [] -> () 40 | | tok :: _ -> return! Error [ FromParser(ParseError("Syntax error", (tok :> ILocatable).Locate())) ] 41 | 42 | let! stmts = 43 | sExprs 44 | |> List.map ( 45 | ParseUntypedAst.parseStmt 46 | >> Result.mapError FromParser 47 | ) 48 | |> List.sequenceResultA 49 | 50 | let funcDefs = 51 | stmts 52 | |> List.map (fun (FuncDefStmt funcDef) -> funcDef) 53 | 54 | let! typedFuncs = 55 | typeFuncDefs funcDefs 56 | |> Result.mapError (fun typeError -> [ FromTypeChecker typeError ]) 57 | 58 | let! wasm = 59 | genCode typedFuncs 60 | |> Result.mapError (List.map FromCodeGen) 61 | 62 | return Wasm.toBytes wasm 63 | } 64 | -------------------------------------------------------------------------------- /src/Waxt.Compiler/Waxt.Compiler.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Waxt.Compiler 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Waxt.Compiler/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsToolkit.ErrorHandling 3 | -------------------------------------------------------------------------------- /src/Waxt.Lexer/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Lexer.Library 3 | 4 | open Waxt.Location 5 | open Waxt.Token 6 | 7 | /// 字句解析中に発生するエラー 8 | type LexError = 9 | private /// キャリッジリターンの直後にラインフィードが続いていない 10 | | LineFeedNotFound of Pos 11 | 12 | module LexError = 13 | let toString = 14 | function 15 | | LineFeedNotFound pos -> 16 | let pos = Pos.toString pos 17 | $"(%s{pos}) Line feed not found" 18 | 19 | let private (|CLeftParen|_|) = 20 | function 21 | | '(' -> Some LeftParen 22 | | _ -> None 23 | 24 | let private (|CRightParen|_|) = 25 | function 26 | | ')' -> Some RightParen 27 | | _ -> None 28 | 29 | let private (|CLeftBracket|_|) = 30 | function 31 | | '[' -> Some LeftBracket 32 | | _ -> None 33 | 34 | let private (|CRightBracket|_|) = 35 | function 36 | | ']' -> Some RightBracket 37 | | _ -> None 38 | 39 | let private (|WhiteSpace|_|) c = 40 | if System.Char.IsWhiteSpace c then 41 | Some() 42 | else 43 | None 44 | 45 | open FsToolkit.ErrorHandling 46 | 47 | /// 与えられた文字列に対して字句解析を行い、成功した場合はトークン列を返す 48 | let lex (str: string) : Result, LexError> = 49 | let rec inner (basePos: Pos) (strAcc: option) (chars: list) : Result, LexError> = 50 | match strAcc, chars with 51 | | None, [] -> Ok [] 52 | 53 | | Some (start, str), [] -> Ok [ Str(str, Range.make start basePos) ] 54 | 55 | | None, ('\n' :: cs | '\r' :: '\n' :: cs) -> inner (Pos.nextLine basePos) None cs 56 | 57 | | Some (start, str), ('\n' :: cs | '\r' :: '\n' :: cs) -> 58 | result { 59 | let! tokens = inner (Pos.nextLine basePos) None cs 60 | 61 | return 62 | Str(str, Range.make start (Pos.previousCol basePos)) 63 | :: tokens 64 | } 65 | 66 | | _, '\r' :: _ -> Error(LineFeedNotFound basePos) 67 | 68 | | None, WhiteSpace :: cs -> inner (Pos.nextCol basePos) None cs 69 | 70 | | Some (start, str), WhiteSpace :: cs -> 71 | result { 72 | let! tokens = inner (Pos.nextCol basePos) None cs 73 | 74 | return 75 | Str(str, Range.make start (Pos.previousCol basePos)) 76 | :: tokens 77 | } 78 | 79 | | None, 80 | (CLeftParen makeTok 81 | | CRightParen makeTok 82 | | CLeftBracket makeTok 83 | | CRightBracket makeTok) :: cs -> 84 | result { 85 | let! tokens = inner (Pos.nextCol basePos) None cs 86 | return makeTok basePos :: tokens 87 | } 88 | 89 | | Some (start, str), 90 | (CLeftParen makeTok 91 | | CRightParen makeTok 92 | | CLeftBracket makeTok 93 | | CRightBracket makeTok) :: cs -> 94 | result { 95 | let! tokens = inner (Pos.nextCol basePos) None cs 96 | 97 | return 98 | Str(str, Range.make start (Pos.previousCol basePos)) 99 | :: makeTok basePos :: tokens 100 | } 101 | 102 | | None, c :: cs -> inner (Pos.nextCol basePos) (Some(basePos, string c)) cs 103 | 104 | | Some (start, str), c :: cs -> inner (Pos.nextCol basePos) (Some(start, str + string c)) cs 105 | 106 | inner Pos.origin None (Seq.toList str) 107 | -------------------------------------------------------------------------------- /src/Waxt.Lexer/Waxt.Lexer.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | Waxt.Lexer 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Waxt.Lexer/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsToolkit.ErrorHandling 3 | -------------------------------------------------------------------------------- /src/Waxt.Location/Locatable.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Location.Locatable 3 | 4 | /// ソースファイル上の位置を特定できることを表す 5 | type ILocatable = 6 | abstract member Locate: unit -> Range 7 | -------------------------------------------------------------------------------- /src/Waxt.Location/Pos.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Location.Pos 3 | 4 | /// ソースファイル上の位置 5 | type Pos = 6 | private 7 | | Pos of line: uint * col: uint 8 | 9 | override this.ToString() = 10 | match this with 11 | | Pos (line, col) -> 12 | let line = line + 1u 13 | let col = col + 1u 14 | $"%i{line}:%i{col}" 15 | 16 | module Pos = 17 | let origin = Pos(0u, 0u) 18 | 19 | let make line col = Pos(line, col) 20 | 21 | let line (Pos (line, _)) = line 22 | 23 | let col (Pos (_, col)) = col 24 | 25 | let toString (pos: Pos) = pos.ToString() 26 | 27 | let nextCol (Pos (line, col)) = Pos(line, col + 1u) 28 | 29 | let previousCol (Pos (line, col)) = Pos(line, col - 1u) 30 | 31 | let nextLine (Pos (line, _)) = Pos(line + 1u, 0u) 32 | -------------------------------------------------------------------------------- /src/Waxt.Location/Range.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Location.Range 3 | 4 | /// ソースファイル上の範囲 5 | type Range = 6 | private 7 | | Range of start: Pos * ``end``: Pos 8 | 9 | override this.ToString() = 10 | match this with 11 | | Range (start, ``end``) -> 12 | let start = Pos.toString start 13 | let ``end`` = Pos.toString ``end`` 14 | $"{start}-{``end``}" 15 | 16 | module Range = 17 | let make start ``end`` = Range(start, ``end``) 18 | 19 | let start (Range (start, _)) = start 20 | 21 | let ``end`` (Range (_, ``end``)) = ``end`` 22 | 23 | let toString (range: Range) = range.ToString() 24 | 25 | let fromPos p = Range(p, p) 26 | -------------------------------------------------------------------------------- /src/Waxt.Location/Waxt.Location.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | Waxt.Location 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Waxt.Location/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /src/Waxt.Parser/Parse.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Parser.Parse 3 | 4 | open Waxt.Location 5 | 6 | type ParseError = ParseError of msg: string * at: Range 7 | 8 | module ParseError = 9 | let toString (ParseError (msg, at)) = 10 | let at = Range.toString at 11 | $"(%s{at}) %s{msg}" 12 | 13 | type ParseResult<'T> = Result<'T, ParseError> 14 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseExpr.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseExpr 2 | 3 | open FsToolkit.ErrorHandling 4 | open Waxt.Location 5 | open Waxt.UntypedAst 6 | 7 | let private (|Int|_|) (str: string) = 8 | match System.Int32.TryParse str with 9 | | true, value -> Some value 10 | | _ -> None 11 | 12 | let private (|I32Literal|_|) (sExpr: SExpr) = 13 | match sExpr with 14 | | Atom (Int value, at) -> Some(I32Const(value, at)) 15 | | _ -> None 16 | 17 | let rec private parseExpr: SExpr -> ParseResult = 18 | function 19 | | I32Literal expr -> Ok expr 20 | | Atom (str, at) -> Ok(Var(name = str, at = at)) 21 | | ParenList ([ Atom ("i32.add", _); lhs; rhs ], at) -> 22 | result { 23 | let! lhs = parseExpr lhs 24 | let! rhs = parseExpr rhs 25 | return I32Add(lhs, rhs, at) 26 | } 27 | | ParenList ([ Atom ("i32.mul", _); lhs; rhs ], at) -> 28 | result { 29 | let! lhs = parseExpr lhs 30 | let! rhs = parseExpr rhs 31 | return I32Mul(lhs, rhs, at) 32 | } 33 | | ParenList ([ Atom ("i32.store", _); addr; content ], at) -> 34 | result { 35 | let! addr = parseExpr addr 36 | let! content = parseExpr content 37 | return I32Store(addr, content, at) 38 | } 39 | | sExpr -> Error(ParseError("Invalid expression", (sExpr :> ILocatable).Locate())) 40 | 41 | let rec parseManyExpr: list -> ParseResult> = 42 | function 43 | | [] -> Ok [] 44 | | sExpr :: rest -> 45 | result { 46 | let! expr = parseExpr sExpr 47 | let! exprs = parseManyExpr rest 48 | return expr :: exprs 49 | } 50 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseFunc.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseFunc 2 | 3 | open FsToolkit.ErrorHandling 4 | open Waxt.Location 5 | open Waxt.Type 6 | open Waxt.UntypedAst 7 | 8 | open ParseExpr 9 | open ParseFuncParams 10 | 11 | let parseFunc (basePos: Pos) : list -> ParseResult = 12 | function 13 | | [] -> Error(ParseError("Expected function name, but reached last element of list", Range.fromPos basePos)) 14 | 15 | | [ Atom (_, range) ] -> Error(ParseError("Expected type name or parameters", Range.fromPos (Range.``end`` range))) 16 | 17 | | Atom (name, nameRange) :: Atom (resultTy, resultTyRange) :: ParenList (parameters, _) :: body -> 18 | result { 19 | let! resultTy = ParseType.parseType resultTyRange resultTy 20 | let! parameters = parseFuncParams parameters 21 | let! body = parseManyExpr body 22 | return FuncDefStmt(FuncDef(FuncName(name, nameRange), resultTy, parameters, body)) 23 | } 24 | 25 | | Atom (name, nameRange) :: ParenList (parameters, _) :: body -> 26 | result { 27 | let! parameters = parseFuncParams parameters 28 | let! body = parseManyExpr body 29 | return FuncDefStmt(FuncDef(FuncName(name, nameRange), Unit, parameters, body)) 30 | } 31 | 32 | | Atom _ :: sExpr :: _ -> 33 | let str = SExpr.toString sExpr 34 | Error(ParseError($"Expected type name or parameters, but got `%s{str}`", (sExpr :> ILocatable).Locate())) 35 | 36 | | sExpr :: _ -> 37 | let str = SExpr.toString sExpr 38 | Error(ParseError($"Expected function name, but got `%s{str}`", (sExpr :> ILocatable).Locate())) 39 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseFuncParams.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseFuncParams 2 | 3 | open FsToolkit.ErrorHandling 4 | open Waxt.Location 5 | open Waxt.Type 6 | 7 | let rec parseFuncParams: list -> ParseResult>> = 8 | function 9 | | [] -> Ok [] 10 | 11 | | Atom (param, at) :: rest -> 12 | result { 13 | let! parameters = parseFuncParams rest 14 | return ((param, at), None) :: parameters 15 | } 16 | 17 | | BracketList ([], at) :: _ -> 18 | Error(ParseError("Expected parameter name or [name : type], but reached last element of list", at)) 19 | 20 | | BracketList ([ Atom (param, paramRange); Atom (":", _); Atom (ty, tyRange) ], _) :: rest -> 21 | result { 22 | let! ty = ParseType.parseType tyRange ty 23 | let! parameters = parseFuncParams rest 24 | 25 | return 26 | ((param, paramRange), Some(ty, tyRange)) 27 | :: parameters 28 | } 29 | 30 | | BracketList (_, at) :: _ 31 | | ParenList (_, at) :: _ -> Error(ParseError("Expected [name : type]", at)) 32 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseSExpr.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseSExpr 2 | 3 | open FsToolkit.ErrorHandling 4 | open Waxt.Location 5 | open Waxt.Token 6 | 7 | let rec parseSExpr (basePos: Pos) : list -> ParseResult> = 8 | function 9 | | [] -> Error(ParseError("Unexpected end of input", Range.fromPos basePos)) 10 | 11 | | (Str (content, at)) :: rest -> Ok(Atom(content, at), rest) 12 | 13 | | LeftParen basePos :: rest -> 14 | result { 15 | match! parseManySExpr basePos rest with 16 | | (_, []) -> return! Error(ParseError("Expected `)`, but reached end of input", Range.fromPos basePos)) 17 | 18 | | (exprs, RightParen ``end`` :: rest) -> return ParenList(exprs, Range.make basePos ``end``), rest 19 | 20 | | (_, tok :: _) -> 21 | let range = (tok :> ILocatable).Locate() 22 | let tok = Token.toString tok 23 | return! Error(ParseError($"Expected `)`, but got `%s{tok}`", range)) 24 | } 25 | 26 | | LeftBracket start :: rest -> 27 | result { 28 | match! parseManySExpr start rest with 29 | | (exprs, RightBracket ``end`` :: rest) -> return BracketList(exprs, Range.make start ``end``), rest 30 | 31 | | (_, []) -> return! Error(ParseError("Expected `]`, but reached end of input", Range.fromPos basePos)) 32 | 33 | | (_, tok :: _) -> 34 | let range = (tok :> ILocatable).Locate() 35 | let tok = Token.toString tok 36 | return! Error(ParseError($"Expected `]`, but got `%s{tok}`", range)) 37 | } 38 | 39 | | tok :: _ -> 40 | let range = (tok :> ILocatable).Locate() 41 | let tok = Token.toString tok 42 | Error(ParseError($"Unexpected token `%s{tok}`", range)) 43 | 44 | and parseManySExpr (basePos: Pos) : list -> ParseResult * list> = 45 | function 46 | | [] -> Ok([], []) 47 | 48 | | tokens -> 49 | match parseSExpr basePos tokens with 50 | | Error _ -> Ok([], tokens) 51 | 52 | | Ok (expr, rest) -> 53 | let basePos = (expr :> ILocatable).Locate() |> Range.``end`` 54 | 55 | result { 56 | let! (exprs, rest) = parseManySExpr basePos rest 57 | return (expr :: exprs, rest) 58 | } 59 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseType.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseType 2 | 3 | open Waxt.Location 4 | open Waxt.Type 5 | 6 | let parseType (range: Range) = 7 | function 8 | | "unit" -> Ok Unit 9 | | "i32" -> Ok I32 10 | | "i64" -> Ok I64 11 | | ty -> Error(ParseError($"Invalid type `%s{ty}`", range)) 12 | -------------------------------------------------------------------------------- /src/Waxt.Parser/ParseUntypedAst.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.ParseUntypedAst 2 | 3 | open Waxt.Location 4 | open Waxt.UntypedAst 5 | 6 | let parseStmt: SExpr -> ParseResult = 7 | function 8 | | (BracketList _ 9 | | Atom _) as sExpr -> 10 | let str = SExpr.toString sExpr 11 | Error(ParseError($"Expected (...) list, but got `%s{str}`", (sExpr :> ILocatable).Locate())) 12 | 13 | | ParenList ([], range) -> Error(ParseError("Expected keyword", Range.fromPos (Range.``end`` range))) 14 | 15 | | ParenList (Atom ("func", range) :: rest, _) -> ParseFunc.parseFunc (Range.``end`` range) rest 16 | 17 | | ParenList (Atom (str, range) :: _, _) -> Error(ParseError($"Invalid keyword `%s{str}`", range)) 18 | 19 | | ParenList (sExpr :: _, _) -> 20 | let str = SExpr.toString sExpr 21 | Error(ParseError($"Expected keyword, but got `%s{str}`", (sExpr :> ILocatable).Locate())) 22 | -------------------------------------------------------------------------------- /src/Waxt.Parser/SExpr.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Parser.SExpr 3 | 4 | open Waxt.Location 5 | 6 | /// S式 7 | type SExpr = 8 | /// S式の構文の最小単位である文字列 9 | | Atom of content: string * at: Range 10 | /// `(`, `)` 括弧で囲われたリスト 11 | | ParenList of members: list * at: Range 12 | /// `[`, `]` 括弧で囲われたリスト 13 | | BracketList of members: list * at: Range 14 | 15 | interface ILocatable with 16 | member this.Locate() = 17 | match this with 18 | | Atom (_, at) 19 | | ParenList (_, at) 20 | | BracketList (_, at) -> at 21 | 22 | module SExpr = 23 | let rec toString = 24 | function 25 | | Atom (content, _) -> content 26 | 27 | | ParenList (list, _) -> 28 | list 29 | |> List.map toString 30 | |> String.concat " " 31 | |> sprintf "(%s)" 32 | 33 | | BracketList (list, _) -> 34 | list 35 | |> List.map toString 36 | |> String.concat " " 37 | |> sprintf "[%s]" 38 | -------------------------------------------------------------------------------- /src/Waxt.Parser/Waxt.Parser.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Waxt.Parser 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Waxt.Parser/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsToolkit.ErrorHandling 3 | -------------------------------------------------------------------------------- /src/Waxt.Token/Library.fs: -------------------------------------------------------------------------------- 1 | namespace Waxt.Token 2 | 3 | open Thoth.Json.Net 4 | open Waxt.Location 5 | 6 | /// 構文の最小単位。字句解析器の出力・構文解析器の入力で用いられる。 7 | type Token = 8 | /// `(` 9 | | LeftParen of Pos 10 | /// `) 11 | | RightParen of Pos 12 | /// `[` 13 | | LeftBracket of Pos 14 | /// `]` 15 | | RightBracket of Pos 16 | /// 括弧または空白文字で区切って得られる、連続した文字列 17 | | Str of raw: string * at: Range 18 | 19 | interface ILocatable with 20 | member this.Locate() = 21 | match this with 22 | | LeftParen pos 23 | | RightParen pos 24 | | LeftBracket pos 25 | | RightBracket pos -> Range.fromPos pos 26 | | Str (_, at) -> at 27 | 28 | module Token = 29 | let toString = 30 | function 31 | | LeftParen _ -> "(" 32 | | RightParen _ -> ")" 33 | | LeftBracket _ -> "[" 34 | | RightBracket _ -> "]" 35 | | Str (str, _) -> str 36 | 37 | let toJson = 38 | function 39 | | LeftParen pos -> 40 | Encode.object [ "token", Encode.string "(" 41 | "at", Encode.string (Pos.toString pos) ] 42 | 43 | | RightParen pos -> 44 | Encode.object [ "token", Encode.string ")" 45 | "at", Encode.string (Pos.toString pos) ] 46 | 47 | | LeftBracket pos -> 48 | Encode.object [ "token", Encode.string "[" 49 | "at", Encode.string (Pos.toString pos) ] 50 | 51 | | RightBracket pos -> 52 | Encode.object [ "token", Encode.string "]" 53 | "at", Encode.string (Pos.toString pos) ] 54 | 55 | | Str (str, at) -> 56 | Encode.object [ "token", Encode.string str 57 | "at", Encode.string (Range.toString at) ] 58 | -------------------------------------------------------------------------------- /src/Waxt.Token/Waxt.Token.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Waxt.Token/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Thoth.Json.Net 3 | -------------------------------------------------------------------------------- /src/Waxt.Type/Type.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Type.Type 3 | 4 | open Thoth.Json.Net 5 | 6 | type Type = 7 | | Unit 8 | | I32 9 | | I64 10 | 11 | module Type = 12 | let equal (a: Type) (b: Type) : bool = 13 | match a, b with 14 | | Unit, Unit -> true 15 | | I32, I32 -> true 16 | | I64, I64 -> true 17 | | _ -> false 18 | 19 | let toString = 20 | function 21 | | Unit _ -> "unit" 22 | | I32 _ -> "i32" 23 | | I64 _ -> "i64" 24 | 25 | let toJson = toString >> Encode.string 26 | -------------------------------------------------------------------------------- /src/Waxt.Type/TypeEnv.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Type.TypeEnv 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.Location 6 | 7 | type TypeEnv = private TypeEnv of typeVars: list<(string * Range) * (Type * Range)> 8 | 9 | module TypeEnv = 10 | let empty = TypeEnv [] 11 | 12 | let add (name: string * Range) (ty: Type * Range) (TypeEnv typeEnv) = TypeEnv((name, ty) :: typeEnv) 13 | 14 | let find (name: string) (TypeEnv typeEnv) : option = 15 | option { 16 | let len = List.length typeEnv 17 | let! index = List.tryFindIndexBack (fun ((name', _), _) -> name = name') typeEnv 18 | return (len - index - 1, snd typeEnv.[index]) 19 | } 20 | -------------------------------------------------------------------------------- /src/Waxt.Type/Waxt.Type.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Waxt.Type 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Waxt.Type/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsToolkit.ErrorHandling 3 | Thoth.Json.Net 4 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/Error.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.Error 3 | 4 | open Waxt.Location 5 | 6 | type TypeError = TypeError of msg: string * at: Range 7 | 8 | module TypeError = 9 | let toString (TypeError (msg, at)) = 10 | let at = Range.toString at 11 | $"(%s{at}) %s{msg}" 12 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/Expect.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.Expect 3 | 4 | open Waxt.Location 5 | open Waxt.Type 6 | 7 | let expectType (expected: Type) ((actual, range): Type * Range) = 8 | if Type.equal expected actual then 9 | Ok() 10 | else 11 | let expected = Type.toString expected 12 | let actual = Type.toString actual 13 | Error(TypeError($"Type mismatch, expected: `%s{expected}`, actual: `%s{actual}`", range)) 14 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.Library 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.TypedAst 6 | open Waxt.UntypedAst 7 | 8 | let typeFuncDefs (funcDefs: seq) : Result = 9 | result { 10 | let! untypedFuncs = typeFuncSigs funcDefs 11 | return! typeFuncBodies untypedFuncs 12 | } 13 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/SeqExt.fs: -------------------------------------------------------------------------------- 1 | module Waxt.TypeChecker.SeqExt 2 | 3 | let iterWhileOk (f: 't -> Result) (sequence: seq<'t>) : Result = 4 | let folder (state: Result) (item: 't) = state |> Result.bind (fun () -> f item) 5 | 6 | sequence |> Seq.fold folder (Ok()) 7 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/TypeExpr.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.TypeExpr 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.Location 6 | open Waxt.Type 7 | open Waxt.TypedAst 8 | open Waxt.UntypedAst 9 | 10 | let rec typeExpr (typeEnv: TypeEnv) : Expr -> Result = 11 | function 12 | | I32Add (lhs, rhs, at) -> 13 | result { 14 | let! lhs = typeExpr typeEnv lhs 15 | do! expectType I32 (TypedExpr.getType lhs, (lhs :> ILocatable).Locate()) 16 | let! rhs = typeExpr typeEnv rhs 17 | do! expectType I32 (TypedExpr.getType rhs, (rhs :> ILocatable).Locate()) 18 | return TypedExpr.I32Add(lhs, rhs, at) 19 | } 20 | 21 | | I32Const (value, at) -> Ok(TypedExpr.I32Const(value, at)) 22 | 23 | | I32Mul (lhs, rhs, at) -> 24 | result { 25 | let! lhs = typeExpr typeEnv lhs 26 | do! expectType I32 (TypedExpr.getType lhs, (lhs :> ILocatable).Locate()) 27 | let! rhs = typeExpr typeEnv rhs 28 | do! expectType I32 (TypedExpr.getType rhs, (rhs :> ILocatable).Locate()) 29 | return TypedExpr.I32Mul(lhs, rhs, at) 30 | } 31 | 32 | | I32Store (addr, content, at) -> 33 | result { 34 | let! addr = typeExpr typeEnv addr 35 | do! expectType I32 (TypedExpr.getType addr, (addr :> ILocatable).Locate()) 36 | let! content = typeExpr typeEnv content 37 | do! expectType I32 (TypedExpr.getType content, (content :> ILocatable).Locate()) 38 | return TypedExpr.I32Store(addr, content, at) 39 | } 40 | 41 | | Var (name, at) -> 42 | result { 43 | let! (index, (ty, _)) = 44 | TypeEnv.find name typeEnv 45 | |> Result.requireSome (TypeError($"{name} is not defined", at)) 46 | 47 | return TypedExpr.Var(index, ty, at) 48 | } 49 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/TypeFuncBodies.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.TypeFuncBodies 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.Location 6 | open Waxt.Type 7 | open Waxt.TypedAst 8 | 9 | let rec private checkProgn 10 | (funcNameRange: Range) 11 | (expectedResultTy: Type) 12 | (exprTypes: list) 13 | : Result = 14 | match exprTypes with 15 | | [] -> 16 | if expectedResultTy = Unit 17 | then Ok () 18 | else Error(TypeError("The result type is not unit, but no expression is found", funcNameRange)) 19 | | [ ty, range ] -> expectType expectedResultTy (ty, range) 20 | | (ty, range) :: rest -> 21 | result { 22 | do! expectType Unit (ty, range) 23 | do! checkProgn funcNameRange expectedResultTy rest 24 | } 25 | 26 | /// 各関数の本体を型付けする 27 | let typeFuncBodies (untypedFuncs: UntypedFuncs) : Result = 28 | let typedFuncs = TypedFuncs(untypedFuncs.Count) 29 | 30 | result { 31 | do! 32 | untypedFuncs 33 | |> SeqExt.iterWhileOk (fun (funcName, (funcSig, body)) -> 34 | let (FuncSig (parameters, resultTy, funcNameRange)) = funcSig 35 | 36 | let typeEnv = 37 | parameters 38 | |> Seq.map (fun (name, (nameRange, (ty, tyRange))) -> ((name, nameRange), (ty, tyRange))) 39 | |> Seq.fold (fun state (name, ty) -> TypeEnv.add name ty state) TypeEnv.empty 40 | 41 | result { 42 | let! typedBody = 43 | body 44 | |> List.map (typeExpr typeEnv) 45 | |> List.sequenceResultM 46 | 47 | do! 48 | checkProgn 49 | funcNameRange 50 | resultTy 51 | (typedBody 52 | |> List.map (fun typedExpr -> 53 | TypedExpr.getType typedExpr, (typedExpr :> ILocatable).Locate())) 54 | 55 | typedFuncs.Add(funcName, (funcSig, typedBody)) 56 | }) 57 | 58 | return typedFuncs 59 | } 60 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/TypeFuncSigs.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypeChecker.TypeFuncSigs 3 | 4 | open FsToolkit.ErrorHandling 5 | open Waxt.Location 6 | open Waxt.Type 7 | open Waxt.TypedAst 8 | open Waxt.UntypedAst 9 | 10 | /// 関数の仮引数リストをチェックし IndexedMap として取得する 11 | let typeFuncParams (parameters: seq<(string * Range) * option>) : Result = 12 | let funcParams = FuncParams(10) 13 | 14 | let registerFuncParam ((paramName, paramNameRange), tyOpt) : Result = 15 | result { 16 | let! (paramType, paramTypeRange) = 17 | tyOpt 18 | |> Result.requireSome (TypeError("Type inference is not currently available", paramNameRange)) 19 | 20 | do! 21 | funcParams[paramName] 22 | |> Result.requireNone (TypeError($"Duplicate parameter `%s{paramName}`", paramNameRange)) 23 | 24 | funcParams.Add(paramName, (paramNameRange, (paramType, paramTypeRange))) 25 | } 26 | 27 | result { 28 | do! parameters |> SeqExt.iterWhileOk registerFuncParam 29 | return funcParams 30 | } 31 | 32 | type UntypedFuncs = IndexedMap> 33 | 34 | /// 各関数のシグネチャをチェックし IndexedMap として取得する 35 | let typeFuncSigs funcDefs : Result = 36 | let sigs = UntypedFuncs(100) 37 | 38 | let registerFuncSig (FuncDef (FuncName (name, at), resultType, parameters, body)) : Result = 39 | result { 40 | do! 41 | sigs[name] 42 | |> Result.requireNone (TypeError($"Duplicate function name `%s{name}`", at)) 43 | 44 | let! funcParams = typeFuncParams parameters 45 | 46 | let funcSig = FuncSig(funcParams, resultType, at) 47 | sigs.Add(name, (funcSig, body)) 48 | } 49 | 50 | result { 51 | do! funcDefs |> SeqExt.iterWhileOk registerFuncSig 52 | return sigs 53 | } 54 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/Waxt.TypeChecker.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Waxt.TypeChecker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Waxt.TypeChecker/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsToolkit.ErrorHandling 3 | -------------------------------------------------------------------------------- /src/Waxt.TypedAst/IndexedMap.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypedAst.IndexedMap 3 | 4 | open System 5 | open System.Collections.Generic 6 | 7 | type IndexedMapEnumerator<'K, 'V> 8 | ( 9 | valuesEnumerator: IEnumerator<'V>, 10 | mappingEnumerator: IEnumerator> 11 | ) = 12 | member _.current 13 | with private get () = (mappingEnumerator.Current.Key, valuesEnumerator.Current) 14 | 15 | interface IDisposable with 16 | member _.Dispose() = 17 | valuesEnumerator.Dispose() 18 | mappingEnumerator.Dispose() 19 | 20 | interface IEnumerator<'K * 'V> with 21 | member this.Current = this.current 22 | 23 | interface Collections.IEnumerator with 24 | member this.Current = this.current 25 | 26 | member _.MoveNext() = 27 | let k = mappingEnumerator.MoveNext() 28 | let v = valuesEnumerator.MoveNext() 29 | 30 | if k <> v then 31 | failwith "Fatal error: illegal state in IndexedMapEnumerator" 32 | 33 | k 34 | 35 | member _.Reset() = 36 | mappingEnumerator.Reset() 37 | valuesEnumerator.Reset() 38 | 39 | /// キーと整数インデックスの両方で要素にアクセス可能なマップ 40 | type IndexedMap<'K, 'V when 'K: comparison> private (values: ResizeArray<'V>, mapping: Dictionary<'K, int>) = 41 | new(capacity: int) = IndexedMap<'K, 'V>(ResizeArray(capacity), new Dictionary<'K, int>()) 42 | 43 | member _.Count = values.Count 44 | 45 | member _.Add(key: 'K, value: 'V) = 46 | mapping.Add(key, values.Count) 47 | values.Add(value) 48 | 49 | member _.Item 50 | with get (key: 'K): option<'V> = 51 | try 52 | Some values[mapping[key]] 53 | with 54 | | :? KeyNotFoundException -> None 55 | | :? ArgumentOutOfRangeException -> failwith "Fatal error: illegal state in IndexedMap" 56 | 57 | member _.TryNth(index: int) : option<'V> = 58 | try 59 | Some values[index] 60 | with 61 | | :? ArgumentOutOfRangeException -> None 62 | 63 | member _.Exists(key: 'K) : bool = mapping.ContainsKey key 64 | 65 | member _.DebugPrint() = 66 | for keyValuePair in mapping do 67 | printfn "%A: %A" keyValuePair.Key values[keyValuePair.Value] 68 | 69 | member private _.getEnumerator() = 70 | new IndexedMapEnumerator<'K, 'V>(values.GetEnumerator(), mapping.GetEnumerator()) 71 | 72 | interface Collections.IEnumerable with 73 | member this.GetEnumerator() = this.getEnumerator () 74 | 75 | interface IEnumerable<'K * 'V> with 76 | member this.GetEnumerator() = this.getEnumerator () 77 | -------------------------------------------------------------------------------- /src/Waxt.TypedAst/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.TypedAst.Library 3 | 4 | open Thoth.Json.Net 5 | open Waxt.Location 6 | open Waxt.Type 7 | 8 | type TypedExpr = 9 | | I32Add of lhs: TypedExpr * rhs: TypedExpr * at: Range 10 | | I32Const of value: int * at: Range 11 | | I32Mul of lhs: TypedExpr * rhs: TypedExpr * at: Range 12 | | I32Store of addr: TypedExpr * content: TypedExpr * at: Range 13 | | Var of index: int * ty: Type * at: Range 14 | 15 | interface ILocatable with 16 | member this.Locate() = 17 | match this with 18 | | I32Add (_, _, at) 19 | | I32Const (_, at) 20 | | I32Mul (_, _, at) 21 | | I32Store (_, _, at) 22 | | Var (_, _, at) -> at 23 | 24 | module TypedExpr = 25 | let rec toJson: TypedExpr -> JsonValue = 26 | function 27 | | I32Add (lhs, rhs, at) -> 28 | Encode.object [ "type", Encode.string "I32Add" 29 | "lhs", toJson lhs 30 | "rhs", toJson rhs 31 | "at", Encode.string (Range.toString at) ] 32 | 33 | | I32Const (value, at) -> 34 | Encode.object [ "type", Encode.string "I32Const" 35 | "value", Encode.int value 36 | "at", Encode.string (Range.toString at) ] 37 | 38 | | I32Mul (lhs, rhs, at) -> 39 | Encode.object [ "type", Encode.string "I32Mul" 40 | "lhs", toJson lhs 41 | "rhs", toJson rhs 42 | "at", Encode.string (Range.toString at) ] 43 | 44 | | I32Store (addr, content, at) -> 45 | Encode.object [ "type", Encode.string "I32Store" 46 | "addr", toJson addr 47 | "content", toJson content 48 | "at", Encode.string (Range.toString at) ] 49 | 50 | | Var (index, ty, at) -> 51 | Encode.object [ "type", Encode.string "Var" 52 | "index", Encode.int index 53 | "ty", Type.toJson ty 54 | "at", Encode.string (Range.toString at) ] 55 | 56 | let getType: TypedExpr -> Type = 57 | function 58 | | I32Add _ 59 | | I32Const _ 60 | | I32Mul _ -> I32 61 | | I32Store _ -> Unit 62 | | Var (_, ty, _) -> ty 63 | 64 | type FuncParams = IndexedMap 65 | 66 | type FuncSig = FuncSig of parameters: FuncParams * result: Type * at: Range 67 | 68 | type TypedFuncs = IndexedMap> 69 | -------------------------------------------------------------------------------- /src/Waxt.TypedAst/Waxt.TypedAst.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Waxt.TypedAst/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Thoth.Json.Net 3 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/Expr.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.UntypedAst.Expr 3 | 4 | open Thoth.Json.Net 5 | open Waxt.Location 6 | 7 | type Expr = 8 | | I32Add of lhs: Expr * rhs: Expr * at: Range 9 | | I32Const of value: int * at: Range 10 | | I32Mul of lhs: Expr * rhs: Expr * at: Range 11 | | I32Store of addr: Expr * content: Expr * at: Range 12 | | Var of name: string * at: Range 13 | 14 | module Expr = 15 | let rec toJson: Expr -> JsonValue = 16 | function 17 | | I32Add (lhs, rhs, at) -> 18 | Encode.object [ "type", Encode.string "I32Add" 19 | "lhs", toJson lhs 20 | "rhs", toJson rhs 21 | "at", Encode.string (Range.toString at) ] 22 | | I32Const (value, at) -> 23 | Encode.object [ "type", Encode.string "I32Const" 24 | "value", Encode.int value 25 | "at", Encode.string (Range.toString at) ] 26 | | I32Mul (lhs, rhs, at) -> 27 | Encode.object [ "type", Encode.string "I32Mul" 28 | "lhs", toJson lhs 29 | "rhs", toJson rhs 30 | "at", Encode.string (Range.toString at) ] 31 | | I32Store (addr, content, at) -> 32 | Encode.object [ "type", Encode.string "I32Store" 33 | "addr", toJson addr 34 | "content", toJson content 35 | "at", Encode.string (Range.toString at) ] 36 | | Var (name, at) -> 37 | Encode.object [ "type", Encode.string "Var" 38 | "name", Encode.string name 39 | "at", Encode.string (Range.toString at) ] 40 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/FuncDef.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.UntypedAst.FuncDef 3 | 4 | open Waxt.Location 5 | open Waxt.Type 6 | 7 | type FuncDef = 8 | | FuncDef of 9 | name: FuncName * 10 | resultTy: Type * 11 | parameters: list<(string * Range) * option> * 12 | body: list 13 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/FuncName.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.UntypedAst.FuncName 3 | 4 | open Thoth.Json.Net 5 | open Waxt.Location 6 | 7 | type FuncName = FuncName of name: string * at: Range 8 | 9 | module FuncName = 10 | let toJson = 11 | function 12 | | FuncName (name, at) -> 13 | Encode.object [ "type", Encode.string "funcName" 14 | "name", Encode.string name 15 | "at", Encode.string (Range.toString at) ] 16 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/Stmt.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.UntypedAst.Stmt 3 | 4 | open Thoth.Json.Net 5 | open Waxt.Location 6 | open Waxt.Type 7 | 8 | type Stmt = FuncDefStmt of FuncDef 9 | 10 | module Stmt = 11 | let toJson: Stmt -> JsonValue = 12 | function 13 | | FuncDefStmt (FuncDef (name, resultTy, parameters, body)) -> 14 | let parameters = 15 | parameters 16 | |> List.map (fun ((name, nameRange), tyOpt) -> 17 | let ty = 18 | tyOpt 19 | |> Option.map (fun (ty, at) -> 20 | [ "type", 21 | Encode.object [ "name", Type.toJson ty 22 | "at", Encode.string (Range.toString at) ] ]) 23 | |> Option.defaultValue [] 24 | 25 | Encode.object ( 26 | [ "name", Encode.string name 27 | "at", Encode.string (Range.toString nameRange) ] 28 | @ ty 29 | )) 30 | |> Encode.list 31 | 32 | let body = body |> List.map Expr.toJson |> Encode.list 33 | 34 | Encode.object ( 35 | [ "type", Encode.string "funcDef" 36 | "name", FuncName.toJson name 37 | "result", Type.toJson resultTy 38 | "parameters", parameters 39 | "body", body ] 40 | ) 41 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/Waxt.UntypedAst.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | Waxt.UntypedAst 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Waxt.UntypedAst/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Thoth.Json.Net 3 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/CodeSection.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.CodeSection 3 | 4 | open Leb128 5 | 6 | type Locals = 7 | | Locals of count: uint32 * ``type``: NumberType 8 | 9 | interface ISerializable with 10 | member this.Serialize() = 11 | match this with 12 | | Locals (count, ``type``) -> 13 | unsignedLeb128 count 14 | @ (``type`` :> ISerializable).Serialize() 15 | 16 | type Inst = 17 | | I32Const of n: int 18 | | I32Add 19 | | I32Mul 20 | | I32Store 21 | | LocalGet of index: uint32 22 | 23 | module Inst = 24 | let toBytes (inst: Inst) : list = 25 | match inst with 26 | | I32Const n -> 0x41uy :: signedLeb128 n 27 | | I32Add -> [ 0x6Auy ] 28 | | I32Mul -> [ 0x6Cuy ] 29 | | I32Store -> 0x36uy :: unsignedLeb128 2u @ unsignedLeb128 0u 30 | | LocalGet index -> 0x20uy :: unsignedLeb128 index 31 | 32 | type Expr = Expr of insts: list 33 | 34 | module Expr = 35 | let toBytes (expr: Expr) : list = 36 | match expr with 37 | | Expr insts -> (insts |> List.collect Inst.toBytes) @ [ 0x0Buy ] 38 | 39 | type Func = 40 | | Func of locals: Vector * expr: Expr 41 | 42 | interface ISerializable with 43 | member this.Serialize() = 44 | match this with 45 | | Func (locals, expr) -> 46 | (locals :> ISerializable).Serialize() 47 | @ (Expr.toBytes expr) 48 | 49 | type Code = 50 | | Code of func: Func 51 | 52 | interface ISerializable with 53 | member this.Serialize() = 54 | match this with 55 | | Code funcs -> 56 | let func = (funcs :> ISerializable).Serialize() 57 | let size = func |> List.length |> uint32 |> unsignedLeb128 58 | size @ func 59 | 60 | type CodeSection = 61 | | CodeSection of Vector 62 | 63 | interface ISection with 64 | member _.Id = 10uy 65 | 66 | member this.GetContents() = 67 | match this with 68 | | CodeSection code -> (code :> ISerializable).Serialize() 69 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/FunctionSection.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.FunctionSection 3 | 4 | type FunctionSection = 5 | | FunctionSection of typeIndices: Vector 6 | 7 | interface ISection with 8 | member _.Id = 0x03uy 9 | 10 | member this.GetContents() = 11 | match this with 12 | | FunctionSection typeIndices -> (typeIndices :> ISerializable).Serialize() 13 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Leb128.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Wasm.Leb128 2 | 3 | open System 4 | 5 | let private clearMsb (x: uint32) = x &&& 127u 6 | 7 | let private addMsbFlags (bytes: list) = 8 | let lastIndex = List.length bytes - 1 9 | 10 | bytes 11 | |> List.mapi (fun index part -> 12 | if index = lastIndex then 13 | part 14 | else 15 | part ||| 128uy) 16 | 17 | let unsignedLeb128 (x: uint32) : list = 18 | let bytes = 19 | (clearMsb x) 20 | :: ([ x >>> 7 21 | x >>> 14 22 | x >>> 21 23 | x >>> 28 ] 24 | |> List.map clearMsb 25 | |> List.takeWhile ((<>) 0u)) 26 | |> List.map uint8 27 | 28 | addMsbFlags bytes 29 | 30 | let private reinterpretIntAsUint32 (x: int) : uint32 = 31 | x 32 | |> BitConverter.GetBytes 33 | |> Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian 34 | 35 | let signedLeb128 (x: int) : list = 36 | if x >= 0 then 37 | unsignedLeb128 (uint32 x) 38 | else 39 | let x = reinterpretIntAsUint32 x 40 | 41 | let bytes = 42 | (clearMsb x) 43 | :: ([ x >>> 7 44 | x >>> 14 45 | x >>> 21 46 | x >>> 28 &&& 112u ] 47 | |> List.map clearMsb 48 | |> List.takeWhile ((<>) 127u)) 49 | |> List.map uint8 50 | 51 | addMsbFlags bytes 52 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Library.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.Library 3 | 4 | open System 5 | 6 | type Wasm = Wasm of typeSection: TypeSection * functionSection: FunctionSection * codeSection: CodeSection 7 | 8 | module Wasm = 9 | let toBytes (Wasm (typeSection, functionSection, codeSection)) : list = 10 | let magic = [ 0x00uy; 0x61uy; 0x73uy; 0x6Duy ] 11 | let version = BitConverter.GetBytes 1u |> List.ofArray 12 | let typeSection = Section.toBytes typeSection 13 | let functionSection = Section.toBytes functionSection 14 | let codeSection = Section.toBytes codeSection 15 | let memorySection = Section.toBytes (MemorySection(Vec [ MemType 1u ])) 16 | 17 | magic 18 | @ version 19 | @ typeSection 20 | @ functionSection @ memorySection @ codeSection 21 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/MemorySection.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.MemorySection 3 | 4 | open Leb128 5 | 6 | type MemType = 7 | | MemType of min: uint32 8 | 9 | interface ISerializable with 10 | member this.Serialize() = 11 | match this with 12 | | MemType min -> 0uy :: unsignedLeb128 min 13 | 14 | type MemorySection = 15 | | MemorySection of memories: Vector 16 | 17 | interface ISection with 18 | member _.Id = 0x05uy 19 | 20 | member this.GetContents() = 21 | match this with 22 | | MemorySection memories -> (memories :> ISerializable).Serialize() 23 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Section.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.Section 3 | 4 | open Leb128 5 | 6 | type ISection = 7 | abstract member Id: byte 8 | abstract member GetContents: unit -> list 9 | 10 | module Section = 11 | let toBytes (section: ISection) : list = 12 | let contents = section.GetContents() 13 | 14 | let len = 15 | contents 16 | |> List.length 17 | |> uint32 18 | |> unsignedLeb128 19 | 20 | section.Id :: len @ contents 21 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Serializable.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.Serializable 3 | 4 | type ISerializable = 5 | abstract member Serialize: unit -> list 6 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Type.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.Type 3 | 4 | type NumberType = 5 | | I32 6 | | I64 7 | | F32 8 | | F64 9 | 10 | interface ISerializable with 11 | member this.Serialize() = 12 | match this with 13 | | I32 -> [ 0x7Fuy ] 14 | | I64 -> [ 0x7Euy ] 15 | | F32 -> [ 0x7Duy ] 16 | | F64 -> [ 0x7Cuy ] 17 | 18 | type FunctionType = 19 | | FunctionType of parameter: Vector * result: Vector 20 | 21 | interface ISerializable with 22 | member this.Serialize() = 23 | match this with 24 | | FunctionType (parameter, result) -> 25 | 0x60uy :: (parameter :> ISerializable).Serialize() 26 | @ (result :> ISerializable).Serialize() 27 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/TypeIndex.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.TypeIndex 3 | 4 | open Leb128 5 | 6 | type TypeIndex = 7 | | TypeIndex of uint32 8 | 9 | interface ISerializable with 10 | member this.Serialize() = 11 | match this with 12 | | TypeIndex index -> unsignedLeb128 index 13 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/TypeSection.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.TypeSection 3 | 4 | type TypeSection = 5 | | TypeSection of funcTypes: Vector 6 | 7 | interface ISection with 8 | member _.Id = 1uy 9 | 10 | member this.GetContents() = 11 | match this with 12 | | TypeSection funcsTypes -> (funcsTypes :> ISerializable).Serialize() 13 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Vector.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Waxt.Wasm.Vector 3 | 4 | open Leb128 5 | 6 | type Vector<'T when 'T :> ISerializable> = 7 | | Vec of list<'T> 8 | 9 | interface ISerializable with 10 | member this.Serialize() = 11 | match this with 12 | | Vec list -> 13 | let len = list |> List.length |> uint32 |> unsignedLeb128 14 | 15 | let elements = 16 | list 17 | |> List.collect (fun elem -> elem.Serialize()) 18 | 19 | len @ elements 20 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/Waxt.Wasm.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Waxt.Wasm/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -uev 3 | 4 | dotnet run --project test/Waxt.Location.Test 5 | dotnet run --project test/Waxt.Lexer.Test 6 | dotnet run --project test/Waxt.Parser.Test 7 | dotnet run --project test/Waxt.TypeChecker.Test 8 | -------------------------------------------------------------------------------- /test/Waxt.Lexer.Test/Program.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Lexer.Test.Program 2 | 3 | open Expecto 4 | open Thoth.Json.Net 5 | open VerifyExpecto 6 | open Waxt.Lexer 7 | open Waxt.Token 8 | 9 | [] 10 | let lexerTest = 11 | testTask "lex" { 12 | let result = 13 | Expect.wantOk (lex "(+ 1 2)") "lexical analysis should succeed" 14 | |> List.map Token.toJson 15 | |> Encode.list 16 | |> Encode.toString 2 17 | 18 | do! Verifier.Verify("lex", result) 19 | } 20 | 21 | [] 22 | let main argv = runTestsInAssembly defaultConfig argv 23 | -------------------------------------------------------------------------------- /test/Waxt.Lexer.Test/Program.lex.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "token": "(", 4 | "at": "1:1" 5 | }, 6 | { 7 | "token": "+", 8 | "at": "1:2-1:2" 9 | }, 10 | { 11 | "token": "1", 12 | "at": "1:4-1:4" 13 | }, 14 | { 15 | "token": "2", 16 | "at": "1:6-1:6" 17 | }, 18 | { 19 | "token": ")", 20 | "at": "1:7" 21 | } 22 | ] -------------------------------------------------------------------------------- /test/Waxt.Lexer.Test/Waxt.Lexer.Test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | Waxt.Lexer.Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Waxt.Lexer.Test/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | FSharp.Core 3 | Thoth.Json.Net 4 | Verify 5 | Verify.Expecto 6 | -------------------------------------------------------------------------------- /test/Waxt.Location.Test/Program.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Location.Test.Program 2 | 3 | open Expecto 4 | open Waxt.Location 5 | 6 | [] 7 | let nextColTest = 8 | test "nextCol" { 9 | let actual = Pos.nextCol (Pos.make 1u 1u) 10 | let expected = Pos.make 1u 2u 11 | Expect.equal actual expected "column should be incremented" 12 | } 13 | 14 | [] 15 | let nextLineTest = 16 | test "nextLine" { 17 | let actual = Pos.nextLine (Pos.make 2u 3u) 18 | let expected = Pos.make 3u 0u 19 | Expect.equal actual expected "column should be 0" 20 | } 21 | 22 | [] 23 | let main argv = runTestsInAssembly defaultConfig argv 24 | -------------------------------------------------------------------------------- /test/Waxt.Location.Test/Waxt.Location.Test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | Waxt.Location.Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/Waxt.Location.Test/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | FSharp.Core 3 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Program.fs: -------------------------------------------------------------------------------- 1 | module Waxt.Parser.Test.Program 2 | 3 | open Expecto 4 | open Thoth.Json.Net 5 | open VerifyExpecto 6 | open Waxt.Lexer 7 | open Waxt.Location 8 | open Waxt.Parser 9 | open Waxt.UntypedAst 10 | 11 | let parsingToSExprShouldSucceed (src: string) = 12 | let (sExpr, rest) = 13 | Expect.wantOk (lex src) "lexical analysis should succeed" 14 | |> ParseSExpr.parseSExpr Pos.origin 15 | |> (fun parseResult -> Expect.wantOk parseResult "Parsing to S-expression should succeed") 16 | 17 | Expect.isEmpty rest "Parser should consume all tokens" 18 | sExpr 19 | 20 | let parsingToStmtShouldSucceed (sExpr: SExpr) = 21 | sExpr 22 | |> ParseUntypedAst.parseStmt 23 | |> (fun parseResult -> Expect.wantOk parseResult "Parsing to statement should succeed") 24 | |> Stmt.toJson 25 | |> Encode.toString 2 26 | 27 | [] 28 | let returnI32 = 29 | testTask "returnI32" { 30 | let sExpr = 31 | parsingToSExprShouldSucceed "(func foo i32 [x i32 y i32] (i32.mul (i32.add 1 2) 4))" 32 | 33 | do! Verifier.Verify("returnI32SExpr", SExpr.toString sExpr) 34 | 35 | let stmt = parsingToStmtShouldSucceed sExpr 36 | 37 | do! Verifier.Verify("returnI32", stmt) 38 | } 39 | 40 | [] 41 | let voidFunc = 42 | testTask "voidFunc" { 43 | let sExpr = 44 | parsingToSExprShouldSucceed "(func add-and-store [addr i32 x i32 y i32] (i32.store addr (i32.add x y)))" 45 | 46 | do! Verifier.Verify("voidFuncSExpr", SExpr.toString sExpr) 47 | 48 | let stmt = parsingToStmtShouldSucceed sExpr 49 | 50 | do! Verifier.Verify("voidFunc", stmt) 51 | } 52 | 53 | [] 54 | let main argv = runTestsInAssembly defaultConfig argv 55 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Program.returnI32.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "type": "funcDef", 3 | "name": { 4 | "type": "funcName", 5 | "name": "foo", 6 | "at": "1:7-1:9" 7 | }, 8 | "result": "i32", 9 | "parameters": [ 10 | { 11 | "name": "x", 12 | "range": "1:16-1:16", 13 | "type": { 14 | "name": "i32", 15 | "range": "1:18-1:20" 16 | } 17 | }, 18 | { 19 | "name": "y", 20 | "range": "1:22-1:22", 21 | "type": { 22 | "name": "i32", 23 | "range": "1:24-1:26" 24 | } 25 | } 26 | ], 27 | "body": [ 28 | { 29 | "type": "I32Mul", 30 | "lhs": { 31 | "type": "I32Add", 32 | "lhs": { 33 | "type": "I32Const", 34 | "value": 1, 35 | "at": "1:47-1:47" 36 | }, 37 | "rhs": { 38 | "type": "I32Const", 39 | "value": 2, 40 | "at": "1:49-1:49" 41 | }, 42 | "at": "1:38-1:50" 43 | }, 44 | "rhs": { 45 | "type": "I32Const", 46 | "value": 4, 47 | "at": "1:52-1:52" 48 | }, 49 | "at": "1:29-1:53" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Program.returnI32SExpr.verified.txt: -------------------------------------------------------------------------------- 1 | (func foo i32 [x i32 y i32] (i32.mul (i32.add 1 2) 4)) 2 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Program.voidFunc.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "type": "funcDef", 3 | "name": { 4 | "type": "funcName", 5 | "name": "add-and-store", 6 | "at": "1:7-1:19" 7 | }, 8 | "result": "unit", 9 | "parameters": [ 10 | { 11 | "name": "addr", 12 | "range": "1:22-1:25", 13 | "type": { 14 | "name": "i32", 15 | "range": "1:27-1:29" 16 | } 17 | }, 18 | { 19 | "name": "x", 20 | "range": "1:31-1:31", 21 | "type": { 22 | "name": "i32", 23 | "range": "1:33-1:35" 24 | } 25 | }, 26 | { 27 | "name": "y", 28 | "range": "1:37-1:37", 29 | "type": { 30 | "name": "i32", 31 | "range": "1:39-1:41" 32 | } 33 | } 34 | ], 35 | "body": [ 36 | { 37 | "type": "I32Store", 38 | "addr": { 39 | "type": "Var", 40 | "name": "addr", 41 | "at": "1:55-1:58" 42 | }, 43 | "content": { 44 | "type": "I32Add", 45 | "lhs": { 46 | "type": "Var", 47 | "name": "x", 48 | "at": "1:69-1:69" 49 | }, 50 | "rhs": { 51 | "type": "Var", 52 | "name": "y", 53 | "at": "1:71-1:71" 54 | }, 55 | "at": "1:60-1:72" 56 | }, 57 | "at": "1:44-1:73" 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Program.voidFuncSExpr.verified.txt: -------------------------------------------------------------------------------- 1 | (func add-and-store [addr i32 x i32 y i32] (i32.store addr (i32.add x y))) 2 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/Waxt.Parser.Test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | Waxt.Parser.Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Waxt.Parser.Test/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | FSharp.Core 3 | Thoth.Json.Net 4 | Verify 5 | Verify.Expecto 6 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/IndexedMap.fs: -------------------------------------------------------------------------------- 1 | module Waxt.TypeChecker.Test.IndexedMap 2 | 3 | open Expecto 4 | open Waxt.TypeChecker 5 | open Waxt.TypedAst 6 | 7 | [] 8 | let indexedMapTest = 9 | test "indexedMap" { 10 | let indexedMap = IndexedMap(2) 11 | Expect.equal indexedMap["foo"] None "IndexedMap should be empty" 12 | Expect.equal indexedMap.Count 0 "Count of IndexedMap should be incremented" 13 | 14 | indexedMap.Add("foo", "fooValue") 15 | Expect.equal indexedMap["foo"] (Some "fooValue") "IndexedMap should contain foo" 16 | Expect.equal (indexedMap.TryNth 0) (Some "fooValue") "foo should be specified by index" 17 | Expect.equal indexedMap.Count 1 "Count of IndexedMap should be incremented" 18 | 19 | indexedMap.Add("bar", "barValue") 20 | Expect.equal indexedMap["bar"] (Some "barValue") "IndexedMap should contain bar" 21 | Expect.equal (indexedMap.TryNth 0) (Some "fooValue") "foo should be specified by index 0" 22 | Expect.equal (indexedMap.TryNth 1) (Some "barValue") "bar should be specified by index 1" 23 | Expect.equal indexedMap.Count 2 "Count of IndexedMap should be incremented" 24 | 25 | let actual = indexedMap |> Seq.toList 26 | 27 | let expected = 28 | [ ("foo", "fooValue") 29 | ("bar", "barValue") ] 30 | 31 | Expect.equal actual expected "Convert IndexedMap to (key, value) list" 32 | } 33 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/Program.fs: -------------------------------------------------------------------------------- 1 | module Waxt.TypeChecker.Test.Program 2 | 3 | open Expecto 4 | open Thoth.Json.Net 5 | open VerifyExpecto 6 | open Waxt.Location 7 | open Waxt.Type 8 | open Waxt.TypeChecker 9 | open Waxt.TypedAst 10 | open Waxt.UntypedAst 11 | 12 | [] 13 | let checkFuncSigsTest = 14 | test "checkFuncSigs" { 15 | let range = Range.fromPos Pos.origin 16 | let funcDef = FuncDef(FuncName("foo", range), I32, [], [ I32Const(12, range) ]) 17 | 18 | Expect.wantOk (typeFuncDefs [ funcDef ]) "compile untyped statements to function signatures" 19 | |> ignore 20 | 21 | Expect.wantError (typeFuncDefs [ funcDef; funcDef ]) "duplicate function definition" 22 | |> ignore 23 | } 24 | 25 | let typeCheckShouldSucceed (typeEnv: TypeEnv) (expr: Expr) = 26 | expr 27 | |> typeExpr typeEnv 28 | |> (fun result -> Expect.wantOk result "The type check should succeed") 29 | |> TypedExpr.toJson 30 | |> Encode.toString 2 31 | 32 | let private range = Range.fromPos Pos.origin 33 | 34 | [] 35 | let i32ConstTest = 36 | testTask "i32Const" { 37 | let result = 38 | I32Const(6, range) 39 | |> typeCheckShouldSucceed TypeEnv.empty 40 | 41 | do! Verifier.Verify("i32Const", result) 42 | } 43 | 44 | [] 45 | let undefinedVarTest = 46 | testTask "undefinedVar" { 47 | let errorMsg = 48 | Var("x", range) 49 | |> typeExpr TypeEnv.empty 50 | |> (fun result -> Expect.wantError result "The type check should fail") 51 | |> TypeError.toString 52 | 53 | do! Verifier.Verify("undefinedVar", errorMsg) 54 | } 55 | 56 | [] 57 | let varTest = 58 | testTask "var" { 59 | let result = 60 | Var("x", range) 61 | |> typeCheckShouldSucceed ( 62 | TypeEnv.empty 63 | |> TypeEnv.add ("x", Range.fromPos Pos.origin) (I32, Range.fromPos Pos.origin) 64 | ) 65 | 66 | do! Verifier.Verify("var", result) 67 | } 68 | 69 | [] 70 | let main argv = runTestsInAssembly defaultConfig argv 71 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/Program.i32Const.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "type": "I32Const", 3 | "value": 6, 4 | "at": "1:1-1:1" 5 | } 6 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/Program.undefinedVar.verified.txt: -------------------------------------------------------------------------------- 1 | (1:1-1:1) x is not defined 2 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/Program.var.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Var", 3 | "index": 0, 4 | "ty": "i32", 5 | "at": "1:1-1:1" 6 | } 7 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/Waxt.TypeChecker.Test.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | Waxt.TypeChecker.Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/Waxt.TypeChecker.Test/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | Thoth.Json.Net 3 | Verify 4 | Verify.Expecto 5 | -------------------------------------------------------------------------------- /waxt.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{86D32CCE-35A2-4362-9510-933A852B7633}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Location", "src\Waxt.Location\Waxt.Location.fsproj", "{D2ECE277-9F00-48BC-841F-6BE2087EB2E6}" 9 | EndProject 10 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Token", "src\Waxt.Token\Waxt.Token.fsproj", "{53808AC5-644D-446C-ACC9-1BB744C0CDAF}" 11 | EndProject 12 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Lexer", "src\Waxt.Lexer\Waxt.Lexer.fsproj", "{3702F038-A268-459C-AC0D-AC1527C13708}" 13 | EndProject 14 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.UntypedAst", "src\Waxt.UntypedAst\Waxt.UntypedAst.fsproj", "{73BA79D3-3E7D-4861-884D-1BF0675BF861}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D819DA91-02BD-4637-B5F3-CBB24D8C83DA}" 17 | EndProject 18 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Lexer.Test", "test\Waxt.Lexer.Test\Waxt.Lexer.Test.fsproj", "{659E5100-BE79-404F-832C-CC1157A40602}" 19 | EndProject 20 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Location.Test", "test\Waxt.Location.Test\Waxt.Location.Test.fsproj", "{760D0E6E-83E0-41BF-8166-8F03D6496F96}" 21 | EndProject 22 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Parser", "src\Waxt.Parser\Waxt.Parser.fsproj", "{64276549-3DAA-4855-990D-81B5E979B2FE}" 23 | EndProject 24 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Parser.Test", "test\Waxt.Parser.Test\Waxt.Parser.Test.fsproj", "{6B4562A5-48A1-4ED0-956E-EB846531F85F}" 25 | EndProject 26 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.TypedAst", "src\Waxt.TypedAst\Waxt.TypedAst.fsproj", "{2445835D-9FA8-4736-9033-25BFABC5A263}" 27 | EndProject 28 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Type", "src\Waxt.Type\Waxt.Type.fsproj", "{6D70DA11-EB15-475C-80EF-3A6529E0FF31}" 29 | EndProject 30 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.TypeChecker", "src\Waxt.TypeChecker\Waxt.TypeChecker.fsproj", "{2C018497-0EC8-48D2-97CC-7091AD6EE198}" 31 | EndProject 32 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Compiler", "src\Waxt.Compiler\Waxt.Compiler.fsproj", "{173EA9F9-26D2-457C-9F27-C99B3958051F}" 33 | EndProject 34 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Cli", "src\Waxt.Cli\Waxt.Cli.fsproj", "{50B21507-9503-411A-9A59-002CDAB66D15}" 35 | EndProject 36 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.TypeChecker.Test", "test\Waxt.TypeChecker.Test\Waxt.TypeChecker.Test.fsproj", "{46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90}" 37 | EndProject 38 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.Wasm", "src\Waxt.Wasm\Waxt.Wasm.fsproj", "{4AF40462-5F81-4957-B2D3-EFF412D19225}" 39 | EndProject 40 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Waxt.CodeGen", "src\Waxt.CodeGen\Waxt.CodeGen.fsproj", "{4FD13B69-23AA-4337-B1EF-755B26E3B252}" 41 | EndProject 42 | Global 43 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 44 | Debug|Any CPU = Debug|Any CPU 45 | Release|Any CPU = Release|Any CPU 46 | EndGlobalSection 47 | GlobalSection(SolutionProperties) = preSolution 48 | HideSolutionNode = FALSE 49 | EndGlobalSection 50 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 51 | {D2ECE277-9F00-48BC-841F-6BE2087EB2E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {D2ECE277-9F00-48BC-841F-6BE2087EB2E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {D2ECE277-9F00-48BC-841F-6BE2087EB2E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {D2ECE277-9F00-48BC-841F-6BE2087EB2E6}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {53808AC5-644D-446C-ACC9-1BB744C0CDAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {53808AC5-644D-446C-ACC9-1BB744C0CDAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {53808AC5-644D-446C-ACC9-1BB744C0CDAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {53808AC5-644D-446C-ACC9-1BB744C0CDAF}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {3702F038-A268-459C-AC0D-AC1527C13708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {3702F038-A268-459C-AC0D-AC1527C13708}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {3702F038-A268-459C-AC0D-AC1527C13708}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {3702F038-A268-459C-AC0D-AC1527C13708}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {73BA79D3-3E7D-4861-884D-1BF0675BF861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {73BA79D3-3E7D-4861-884D-1BF0675BF861}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {73BA79D3-3E7D-4861-884D-1BF0675BF861}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {73BA79D3-3E7D-4861-884D-1BF0675BF861}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {659E5100-BE79-404F-832C-CC1157A40602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {659E5100-BE79-404F-832C-CC1157A40602}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {659E5100-BE79-404F-832C-CC1157A40602}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {659E5100-BE79-404F-832C-CC1157A40602}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {760D0E6E-83E0-41BF-8166-8F03D6496F96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {760D0E6E-83E0-41BF-8166-8F03D6496F96}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {760D0E6E-83E0-41BF-8166-8F03D6496F96}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {760D0E6E-83E0-41BF-8166-8F03D6496F96}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {64276549-3DAA-4855-990D-81B5E979B2FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 76 | {64276549-3DAA-4855-990D-81B5E979B2FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 77 | {64276549-3DAA-4855-990D-81B5E979B2FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {64276549-3DAA-4855-990D-81B5E979B2FE}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {6B4562A5-48A1-4ED0-956E-EB846531F85F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {6B4562A5-48A1-4ED0-956E-EB846531F85F}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {6B4562A5-48A1-4ED0-956E-EB846531F85F}.Release|Any CPU.ActiveCfg = Release|Any CPU 82 | {6B4562A5-48A1-4ED0-956E-EB846531F85F}.Release|Any CPU.Build.0 = Release|Any CPU 83 | {2445835D-9FA8-4736-9033-25BFABC5A263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {2445835D-9FA8-4736-9033-25BFABC5A263}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {2445835D-9FA8-4736-9033-25BFABC5A263}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {2445835D-9FA8-4736-9033-25BFABC5A263}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {6D70DA11-EB15-475C-80EF-3A6529E0FF31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 88 | {6D70DA11-EB15-475C-80EF-3A6529E0FF31}.Debug|Any CPU.Build.0 = Debug|Any CPU 89 | {6D70DA11-EB15-475C-80EF-3A6529E0FF31}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {6D70DA11-EB15-475C-80EF-3A6529E0FF31}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {2C018497-0EC8-48D2-97CC-7091AD6EE198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {2C018497-0EC8-48D2-97CC-7091AD6EE198}.Debug|Any CPU.Build.0 = Debug|Any CPU 93 | {2C018497-0EC8-48D2-97CC-7091AD6EE198}.Release|Any CPU.ActiveCfg = Release|Any CPU 94 | {2C018497-0EC8-48D2-97CC-7091AD6EE198}.Release|Any CPU.Build.0 = Release|Any CPU 95 | {173EA9F9-26D2-457C-9F27-C99B3958051F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 96 | {173EA9F9-26D2-457C-9F27-C99B3958051F}.Debug|Any CPU.Build.0 = Debug|Any CPU 97 | {173EA9F9-26D2-457C-9F27-C99B3958051F}.Release|Any CPU.ActiveCfg = Release|Any CPU 98 | {173EA9F9-26D2-457C-9F27-C99B3958051F}.Release|Any CPU.Build.0 = Release|Any CPU 99 | {50B21507-9503-411A-9A59-002CDAB66D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 100 | {50B21507-9503-411A-9A59-002CDAB66D15}.Debug|Any CPU.Build.0 = Debug|Any CPU 101 | {50B21507-9503-411A-9A59-002CDAB66D15}.Release|Any CPU.ActiveCfg = Release|Any CPU 102 | {50B21507-9503-411A-9A59-002CDAB66D15}.Release|Any CPU.Build.0 = Release|Any CPU 103 | {46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 104 | {46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90}.Debug|Any CPU.Build.0 = Debug|Any CPU 105 | {46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90}.Release|Any CPU.ActiveCfg = Release|Any CPU 106 | {46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90}.Release|Any CPU.Build.0 = Release|Any CPU 107 | {4AF40462-5F81-4957-B2D3-EFF412D19225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 108 | {4AF40462-5F81-4957-B2D3-EFF412D19225}.Debug|Any CPU.Build.0 = Debug|Any CPU 109 | {4AF40462-5F81-4957-B2D3-EFF412D19225}.Release|Any CPU.ActiveCfg = Release|Any CPU 110 | {4AF40462-5F81-4957-B2D3-EFF412D19225}.Release|Any CPU.Build.0 = Release|Any CPU 111 | {4FD13B69-23AA-4337-B1EF-755B26E3B252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 112 | {4FD13B69-23AA-4337-B1EF-755B26E3B252}.Debug|Any CPU.Build.0 = Debug|Any CPU 113 | {4FD13B69-23AA-4337-B1EF-755B26E3B252}.Release|Any CPU.ActiveCfg = Release|Any CPU 114 | {4FD13B69-23AA-4337-B1EF-755B26E3B252}.Release|Any CPU.Build.0 = Release|Any CPU 115 | EndGlobalSection 116 | GlobalSection(NestedProjects) = preSolution 117 | {D2ECE277-9F00-48BC-841F-6BE2087EB2E6} = {86D32CCE-35A2-4362-9510-933A852B7633} 118 | {53808AC5-644D-446C-ACC9-1BB744C0CDAF} = {86D32CCE-35A2-4362-9510-933A852B7633} 119 | {3702F038-A268-459C-AC0D-AC1527C13708} = {86D32CCE-35A2-4362-9510-933A852B7633} 120 | {73BA79D3-3E7D-4861-884D-1BF0675BF861} = {86D32CCE-35A2-4362-9510-933A852B7633} 121 | {659E5100-BE79-404F-832C-CC1157A40602} = {D819DA91-02BD-4637-B5F3-CBB24D8C83DA} 122 | {760D0E6E-83E0-41BF-8166-8F03D6496F96} = {D819DA91-02BD-4637-B5F3-CBB24D8C83DA} 123 | {64276549-3DAA-4855-990D-81B5E979B2FE} = {86D32CCE-35A2-4362-9510-933A852B7633} 124 | {6B4562A5-48A1-4ED0-956E-EB846531F85F} = {D819DA91-02BD-4637-B5F3-CBB24D8C83DA} 125 | {2445835D-9FA8-4736-9033-25BFABC5A263} = {86D32CCE-35A2-4362-9510-933A852B7633} 126 | {6D70DA11-EB15-475C-80EF-3A6529E0FF31} = {86D32CCE-35A2-4362-9510-933A852B7633} 127 | {2C018497-0EC8-48D2-97CC-7091AD6EE198} = {86D32CCE-35A2-4362-9510-933A852B7633} 128 | {173EA9F9-26D2-457C-9F27-C99B3958051F} = {86D32CCE-35A2-4362-9510-933A852B7633} 129 | {50B21507-9503-411A-9A59-002CDAB66D15} = {86D32CCE-35A2-4362-9510-933A852B7633} 130 | {46CD41A5-7DEF-4A7A-A979-FFB8C2EEAA90} = {D819DA91-02BD-4637-B5F3-CBB24D8C83DA} 131 | {4AF40462-5F81-4957-B2D3-EFF412D19225} = {86D32CCE-35A2-4362-9510-933A852B7633} 132 | {4FD13B69-23AA-4337-B1EF-755B26E3B252} = {86D32CCE-35A2-4362-9510-933A852B7633} 133 | EndGlobalSection 134 | EndGlobal 135 | --------------------------------------------------------------------------------