diff --git a/backend/src/LibExecution/Interpreter.fs b/backend/src/LibExecution/Interpreter.fs index c42081d34a..90a86f6bfe 100644 --- a/backend/src/LibExecution/Interpreter.fs +++ b/backend/src/LibExecution/Interpreter.fs @@ -120,6 +120,57 @@ let rec execute vmState.registers[copyTo] <- vmState.registers[copyFrom] return! execute exeState vmState (counter + 1) + | MatchValue(valueReg, pat, failJump) -> + let matches, vars = + match pat, vmState.registers[valueReg] with + | MPUnit, DUnit -> true, [] + + | MPBool l, DBool r -> l = r, [] + + | MPInt8 l, DInt8 r -> l = r, [] + | MPUInt8 l, DUInt8 r -> l = r, [] + | MPInt16 l, DInt16 r -> l = r, [] + | MPUInt16 l, DUInt16 r -> l = r, [] + | MPInt32 l, DInt32 r -> l = r, [] + | MPUInt32 l, DUInt32 r -> l = r, [] + | MPInt64 l, DInt64 r -> l = r, [] + | MPUInt64 l, DUInt64 r -> l = r, [] + | MPInt128 l, DInt128 r -> l = r, [] + | MPUInt128 l, DUInt128 r -> l = r, [] + + | MPFloat l, DFloat r -> l = r, [] + + | MPChar l, DChar r -> l = r, [] + | MPString l, DString r -> l = r, [] + + // TODO + // | MPList of List + + // | MPListCons of head : MatchPattern * tail : MatchPattern + + // | MPTuple of + // first : MatchPattern * + // second : MatchPattern * + // theRest : List + + | MPVariable name, dv -> true, [ (name, dv) ] + + | _ -> false, [] + + if matches then + let vmState = + vars + |> List.fold + (fun vmState (varName, value) -> + { vmState with + symbolTable = Map.add varName value vmState.symbolTable }) + vmState + return! execute exeState vmState (counter + 1) + else + return! execute exeState vmState (counter + failJump + 1) + + + | ExtractTupleItems(extractFrom, firstReg, secondReg, restRegs) -> match vmState.registers[extractFrom] with | DTuple(first, second, rest) -> @@ -133,6 +184,7 @@ let rec execute | _ -> return DString "Error: Expected a tuple for decomposition" | Fail _rte -> return DUnit // TODO + | MatchUnmatched -> return DUnit // TODO } diff --git a/backend/src/LibExecution/ProgramTypes.fs b/backend/src/LibExecution/ProgramTypes.fs index cec0dffedd..d537c4f47e 100644 --- a/backend/src/LibExecution/ProgramTypes.fs +++ b/backend/src/LibExecution/ProgramTypes.fs @@ -124,35 +124,35 @@ type LetPattern = theRest : List | LPVariable of id * name : string -// /// Used for pattern matching in a match statement -// type MatchPattern = -// | MPUnit of id +/// Used for pattern matching in a match statement +type MatchPattern = + | MPUnit of id -// | MPBool of id * bool + | MPBool of id * bool -// | MPInt8 of id * int8 -// | MPUInt8 of id * uint8 -// | MPInt16 of id * int16 -// | MPUInt16 of id * uint16 -// | MPInt32 of id * int32 -// | MPUInt32 of id * uint32 -// | MPInt64 of id * int64 -// | MPUInt64 of id * uint64 -// | MPInt128 of id * System.Int128 -// | MPUInt128 of id * System.UInt128 + | MPInt8 of id * int8 + | MPUInt8 of id * uint8 + | MPInt16 of id * int16 + | MPUInt16 of id * uint16 + | MPInt32 of id * int32 + | MPUInt32 of id * uint32 + | MPInt64 of id * int64 + | MPUInt64 of id * uint64 + | MPInt128 of id * System.Int128 + | MPUInt128 of id * System.UInt128 -// | MPFloat of id * Sign * string * string + | MPFloat of id * Sign * string * string -// | MPChar of id * string -// | MPString of id * string + | MPChar of id * string + | MPString of id * string -// | MPList of id * List -// | MPListCons of id * head : MatchPattern * tail : MatchPattern -// | MPTuple of id * MatchPattern * MatchPattern * List + | MPList of id * List + | MPListCons of id * head : MatchPattern * tail : MatchPattern + | MPTuple of id * MatchPattern * MatchPattern * List -// | MPEnum of id * caseName : string * fieldPats : List + //| MPEnum of id * caseName : string * fieldPats : List -// | MPVariable of id * string + | MPVariable of id * string type BinaryOperation = | BinOpAnd @@ -260,15 +260,15 @@ type Expr = // /// `(1 + 2) |> fnName |> (+) 3` // | EPipe of id * Expr * List - // /// Supports `match` expressions - // /// ```fsharp - // /// match x + 2 with // arg - // /// | pattern -> expr // cases[0] - // /// | pattern -> expr - // /// | ... - // /// ``` - // // cases is a list to represent when a user starts typing but doesn't complete it - // | EMatch of id * arg : Expr * cases : List + /// Supports `match` expressions + /// ```fsharp + /// match x + 2 with // arg + /// | pattern -> expr // cases[0] + /// | pattern -> expr + /// | ... + /// ``` + // cases is a list to represent when a user starts typing but doesn't complete it + | EMatch of id * arg : Expr * cases : List // // Composed of binding pattern, the expression to create bindings for, @@ -353,7 +353,7 @@ type Expr = // NameResolution -//and MatchCase = { pat : MatchPattern; whenCondition : Option; rhs : Expr } +and MatchCase = { pat : MatchPattern; whenCondition : Option; rhs : Expr } and StringSegment = | StringText of string @@ -409,7 +409,7 @@ module Expr = // | ERecord(id, _, _) // | ERecordUpdate(id, _, _) // | EEnum(id, _, _, _) - // | EMatch(id, _, _) + | EMatch(id, _, _) -> id // module PipeExpr = diff --git a/backend/src/LibExecution/ProgramTypesToRuntimeTypes.fs b/backend/src/LibExecution/ProgramTypesToRuntimeTypes.fs index a36f01d3c1..9cf078dcd1 100644 --- a/backend/src/LibExecution/ProgramTypesToRuntimeTypes.fs +++ b/backend/src/LibExecution/ProgramTypesToRuntimeTypes.fs @@ -142,6 +142,9 @@ module LetPattern = | PT.LPTuple(_id, first, second, theRest) -> // reserve the first two registers + // TODO: why do we actually need registers, when we're just assigning variables? + // If RT.LetPattern were more like RT.MatchPattern, we could simply have one instruction that + // assigns the variables in one fell swoop, failing if anything doesn't deconstruct properly. let firstReg, secondReg, rc = rc, rc + 1, rc + 2 let (rcAfterFirst, firstInstrs) = toRT rc first firstReg @@ -166,39 +169,88 @@ module LetPattern = +module MatchPattern = + let rec toRT (p : PT.MatchPattern) : RT.MatchPattern = + match p with + | PT.MPUnit _ -> RT.MPUnit + | PT.MPBool(_, b) -> RT.MPBool b -// module MatchPattern = -// let rec toRT (p : PT.MatchPattern) : RT.MatchPattern = -// match p with -// | PT.MPVariable(id, str) -> RT.MPVariable(id, str) -// | PT.MPEnum(id, caseName, fieldPats) -> -// RT.MPEnum(id, caseName, List.map toRT fieldPats) -// | PT.MPInt64(id, i) -> RT.MPInt64(id, i) -// | PT.MPUInt64(id, i) -> RT.MPUInt64(id, i) -// | PT.MPInt8(id, i) -> RT.MPInt8(id, i) -// | PT.MPUInt8(id, i) -> RT.MPUInt8(id, i) -// | PT.MPInt16(id, i) -> RT.MPInt16(id, i) -// | PT.MPUInt16(id, i) -> RT.MPUInt16(id, i) -// | PT.MPInt32(id, i) -> RT.MPInt32(id, i) -// | PT.MPUInt32(id, i) -> RT.MPUInt32(id, i) -// | PT.MPInt128(id, i) -> RT.MPInt128(id, i) -// | PT.MPUInt128(id, i) -> RT.MPUInt128(id, i) -// | PT.MPBool(id, b) -> RT.MPBool(id, b) -// | PT.MPChar(id, c) -> RT.MPChar(id, c) -// | PT.MPString(id, s) -> RT.MPString(id, s) -// | PT.MPFloat(id, s, w, f) -> -// let w = if w = "" then "0" else w -// RT.MPFloat(id, makeFloat s w f) -// | PT.MPUnit id -> RT.MPUnit id -// | PT.MPTuple(id, first, second, theRest) -> -// RT.MPTuple(id, toRT first, toRT second, List.map toRT theRest) -// | PT.MPList(id, pats) -> RT.MPList(id, List.map toRT pats) -// | PT.MPListCons(id, head, tail) -> RT.MPListCons(id, toRT head, toRT tail) + | PT.MPInt8(_, i) -> RT.MPInt8 i + | PT.MPUInt8(_, i) -> RT.MPUInt8 i + | PT.MPInt16(_, i) -> RT.MPInt16 i + | PT.MPUInt16(_, i) -> RT.MPUInt16 i + | PT.MPInt32(_, i) -> RT.MPInt32 i + | PT.MPUInt32(_, i) -> RT.MPUInt32 i + | PT.MPInt64(_, i) -> RT.MPInt64 i + | PT.MPUInt64(_, i) -> RT.MPUInt64 i + | PT.MPInt128(_, i) -> RT.MPInt128 i + | PT.MPUInt128(_, i) -> RT.MPUInt128 i + + | PT.MPFloat(_, sign, whole, frac) -> RT.MPFloat(makeFloat sign whole frac) + + | PT.MPChar(_, c) -> RT.MPChar c + | PT.MPString(_, s) -> RT.MPString s + + | PT.MPList(_, pats) -> RT.MPList(List.map toRT pats) + | PT.MPListCons(_, head, tail) -> RT.MPListCons(toRT head, toRT tail) + + | PT.MPTuple(_, first, second, theRest) -> + RT.MPTuple(toRT first, toRT second, List.map toRT theRest) + + | PT.MPVariable(_, name) -> RT.MPVariable name + + + let toMatchInstr + (p : PT.MatchPattern) + (valueReg : RT.Register) + (jumpByFail) + : RT.Instruction = + RT.MatchValue(valueReg, toRT p, jumpByFail) + + +module MatchCase = + /// Compiling a MatchCase happens in two phases, because many instructions + /// require knowing how many instructions to jump over, which we can't know + /// until we know the basics of all the cases. + /// + /// This type holds all the information we gather as part of the first phase + /// , in order of where the instrs should be at the end of the second phase. + /// + /// Note: not represented here, we'll also need an unconditional `JumpBy` instr + /// , to get past all the cases. We can only determine how many instrs to jump + /// after the first phases is complete, but it'll land at the end of these. + type IntermediateValue = + { + /// jumpByFail -> instr + /// `RT.MatchValue(valueReg, pat, jumpByFail)` + /// (the `pat` and `valueReg` are known in the first phase) + matchValueInstrFn : int -> RT.Instruction + + /// Evaluation of the `whenCondition` (if it exists -- might be empty) + whenCondInstructions : RT.Instructions + + /// (jumpBy) -> instr + /// `RT.JumpByIfFalse(jumpBy, whenCondResultReg)` + /// (`whenCondResultReg` is known in the first phase) + whenCondJump : Option RT.Instruction> + + /// Evaluation of the RHS + /// + /// Includes `CopyVal(resultReg, rhsResultReg)` + rhsInstrs : RT.Instructions + + /// RC after all instructions + /// + /// Note: Different branches/cases will require different # of registers + /// , so we'll end up taking the max of all the RCs + rc : int + } module Expr = // CLEANUP clearly not the most efficient to do this, but probably fine for now + // TODO ok this is actually really wasteful. a single text string segment could be a single instruction let rec compileString (rc : int) (segments : List) @@ -251,6 +303,7 @@ module Expr = | PT.EString(_id, segments) -> compileString rc segments + | PT.EList(_id, items) -> let listReg = rc let init = (rc + 1, [ RT.LoadVal(listReg, RT.DList(VT.unknown, [])) ]) @@ -266,6 +319,7 @@ module Expr = (regCounter, instrs, listReg) + | PT.EDict(_id, items) -> let dictReg = rc let init = (rc + 1, [ RT.LoadVal(dictReg, RT.DDict(VT.unknown, Map.empty)) ]) @@ -281,6 +335,7 @@ module Expr = (regCounter, instrs, dictReg) + | PT.ETuple(_id, first, second, theRest) -> // save the 'first' register for the result let tupleReg, rc = rc, rc + 1 @@ -384,6 +439,7 @@ module Expr = // (which should fail when we apply it) (rc, [ RT.Fail(RT.RuntimeError.oldError "Couldn't find fn") ], rc) + | PT.EApply(_id, thingToApplyExpr, typeArgs, args) -> let (regCounter, thingToApplyInstrs, thingToApplyReg) = // (usually, a fn name) @@ -413,6 +469,96 @@ module Expr = (regCounter + 1, thingToApplyInstrs @ argInstrs @ [ callInstr ], putResultIn) + | PT.EMatch(_id, expr, cases) -> + // first, the easy part - compile the expression we're `match`ing against. + let (rcAfterExpr, exprInstrs, exprResultReg) = toRT rc expr + + // Shortly, we'll compile each of the cases. + // We'll use this `resultReg` to store the final result of the match + // , so we have a consistent place to look for it. + // (similar to how we handle `EIf` -- refer to that for a simpler example) + let resultReg, rcAfterResult = rcAfterExpr, rcAfterExpr + 1 + + // We compile each `case` in two phases, because some instrs require knowing + // how many instrs to jump over, which we can't know until we know the basics + // of all the cases. + // + // See `MatchCase.IntermediateValue` for more info. + let casesAfterFirstPhase : List = + cases + |> List.map (fun c -> + // compile the `when` condition, if it exists, as much as we can + let rcAfterWhenCond, whenCondInstrs, whenCondJump = + match c.whenCondition with + | None -> (rcAfterResult, [], None) + | Some whenCond -> + let (rcAfterWhenCond, whenCondInstrs, whenCondReg) = + toRT rcAfterResult whenCond + (rcAfterWhenCond, + whenCondInstrs, + Some(fun jumpBy -> RT.JumpByIfFalse(jumpBy, whenCondReg))) + + // compile the `rhs` of the case + let rcAfterRhs, rhsInstrs, rhsResultReg = toRT rcAfterWhenCond c.rhs + + // return the intermediate results, as far along as they are + { matchValueInstrFn = MatchPattern.toMatchInstr c.pat exprResultReg + whenCondInstructions = whenCondInstrs + whenCondJump = whenCondJump + rhsInstrs = rhsInstrs @ [ RT.CopyVal(resultReg, rhsResultReg) ] + rc = rcAfterRhs }) + + let countInstrsForCase (c : MatchCase.IntermediateValue) : int = + 1 // for the `MatchValue` instruction + + List.length c.whenCondInstructions + + (match c.whenCondJump with + | Some _ -> 1 + | None -> 0) + + List.length c.rhsInstrs + + 1 // for the `JumpBy` instruction + + let (cases, _) : List * int = + casesAfterFirstPhase + |> List.map (fun c -> + let instrCount = countInstrsForCase c + (c, instrCount)) + |> List.foldRight + // CLEANUP this works, but hurts the brain a bit. + (fun (acc, runningTotal) (c, instrCount) -> + let newTotal = runningTotal + instrCount + (acc @ [ c, runningTotal ], newTotal)) + ([], 0) + let cases = List.rev cases + + + let caseInstrs = + cases + |> List.fold + (fun instrs (c, instrsAfterThisCaseUntilEndOfMatch) -> + // note: `instrsAfterThisCaseUntilEndOfMatch` does not include + // the final MatchUnmatched instruction + + let caseInstrs = + [ c.matchValueInstrFn (countInstrsForCase c - 1) ] // -1 because we can skip over the MatchValue instr + @ c.whenCondInstructions + @ (match c.whenCondJump with + // jump to next case if the when condition is false + | Some jump -> [ jump (List.length c.rhsInstrs + 1) ] + | None -> []) + @ c.rhsInstrs + @ [ RT.JumpBy(instrsAfterThisCaseUntilEndOfMatch + 1) ] + + instrs @ caseInstrs) + [] + + + let instrs = exprInstrs @ caseInstrs @ [ RT.MatchUnmatched ] + + let rcAtEnd = casesAfterFirstPhase |> List.map _.rc |> List.max + + (rcAtEnd, instrs, resultReg) + + // let rec toRT (e : PT.Expr) : RT.Instructions = // match e with // // | PT.EConstant(id, Ok name) -> RT.EConstant(id, FQConstantName.toRT name) diff --git a/backend/src/LibExecution/RuntimeTypes.fs b/backend/src/LibExecution/RuntimeTypes.fs index 4ca2e07adf..1959102396 100644 --- a/backend/src/LibExecution/RuntimeTypes.fs +++ b/backend/src/LibExecution/RuntimeTypes.fs @@ -502,10 +502,38 @@ and TypeReference = and Register = int // // TODO: unit of measure -// TODO: consider if each of these should include the Expr ID that they came from -// -// Would Expr ID be enough? -// I don't _think_ we'd have to note the fn ID or TL ID or script name, but maybe?) +and MatchPattern = + | MPUnit + | MPBool of bool + | MPInt8 of int8 + | MPUInt8 of uint8 + | MPInt16 of int16 + | MPUInt16 of uint16 + | MPInt32 of int32 + | MPUInt32 of uint32 + | MPInt64 of int64 + | MPUInt64 of uint64 + | MPInt128 of System.Int128 + | MPUInt128 of System.UInt128 + | MPFloat of float + | MPChar of string + | MPString of string + | MPList of List + | MPListCons of head : MatchPattern * tail : MatchPattern // TODO: but the tail is a list... + | MPTuple of + first : MatchPattern * + second : MatchPattern * + theRest : List + | MPVariable of string + +/// TODO: consider if each of these should include the Expr ID that they came from +/// +/// Would Expr ID be enough? +/// I don't _think_ we'd have to note the fn ID or TL ID or script name, but maybe?) +/// +/// We could also record the Instruction Index -> ExprID mapping _adjacent_ to RT, +/// and only load it when needed. +/// That way, the Interpreter could be lighter-weight. and Instruction = /// Push a ("constant") value into a register | LoadVal of loadTo : Register * Dval @@ -568,6 +596,19 @@ and Instruction = /// Fail if this is hit (basically "raise an exception") | Fail of RuntimeError + /// Check if the value in the noted register the noted pattern, + /// and extract vars per MPVariable as relevant. + | MatchValue of + valueReg : Register * // what we're matching against + pat : MatchPattern * + //successJump : int * + failJump : int + + /// Could not find matching case in a match expression + /// CLEANUP we probably need a way to reference back to PT so we can get useful RTEs + /// TODO maybe make this a special case of Fail + | MatchUnmatched + and Instructions = List and InstructionsWithContext = diff --git a/backend/tests/TestUtils/PTShortcuts.fs b/backend/tests/TestUtils/PTShortcuts.fs index e85ac0d049..35603a5d74 100644 --- a/backend/tests/TestUtils/PTShortcuts.fs +++ b/backend/tests/TestUtils/PTShortcuts.fs @@ -23,15 +23,35 @@ let eFloat (sign : Sign) (whole : string) (fraction : string) : Expr = EFloat(gid (), sign, whole, fraction) let eChar (c : string) : Expr = EChar(gid (), c) -let eStr (str : string) : Expr = EString(gid (), [ StringText str ]) - - +let strText (str : string) : StringSegment = StringText str +let strInterp (expr : Expr) : StringSegment = StringInterpolation expr +let eStr (segments : List) : Expr = EString(gid (), segments) let eList (elems : Expr list) : Expr = EList(gid (), elems) - +let eDict (entries : List) : Expr = EDict(gid (), entries) +let eTuple (first : Expr) (second : Expr) (theRest : Expr list) : Expr = + ETuple(gid (), first, second, theRest) + + +let lpUnit () : LetPattern = LPUnit(gid ()) +let lpVar (name : string) : LetPattern = LPVariable(gid (), name) +let lpTuple + (first : LetPattern) + (second : LetPattern) + (theRest : LetPattern list) + : LetPattern = + LPTuple(gid (), first, second, theRest) +let eLet (pat : LetPattern) (value : Expr) (body : Expr) : Expr = + ELet(gid (), pat, value, body) let eVar (name : string) : Expr = EVariable(gid (), name) +let eIf (cond : Expr) (thenBranch : Expr) (elseBranch : Option) : Expr = + EIf(gid (), cond, thenBranch, elseBranch) + +let eMatch (expr : Expr) (cases : List) : Expr = + EMatch(gid (), expr, cases) + // let eFieldAccess (expr : Expr) (fieldName : string) : Expr = // ERecordFieldAccess(gid (), expr, fieldName) @@ -79,8 +99,7 @@ let eApply let args = NEList.ofListUnsafe "eApply" [] args EApply(gid (), target, typeArgs, args) -// let eTuple (first : Expr) (second : Expr) (theRest : Expr list) : Expr = -// ETuple(gid (), first, second, theRest) + // let customTypeRecord (fields : List) : TypeDeclaration.T = diff --git a/backend/tests/Tests/Interpreter.Tests.fs b/backend/tests/Tests/Interpreter.Tests.fs index 2b9b0f2c33..0f31f21fe8 100644 --- a/backend/tests/Tests/Interpreter.Tests.fs +++ b/backend/tests/Tests/Interpreter.Tests.fs @@ -21,146 +21,89 @@ let eval pt = return! LibExecution.Interpreter.eval executionState vmState } - -let onePlusTwo = - testTask "1+2" { - let! actual = eval E.onePlusTwo |> Ply.toTask - let expected = RT.DInt64 3L +let t name expr expected = + testTask name { + let! actual = eval expr |> Ply.toTask return Expect.equal actual expected "" } + +let onePlusTwo = t "1+2" E.onePlusTwo (RT.DInt64 3L) + let boolList = - testTask "[true; false; true]" { - let! actual = eval E.boolList |> Ply.toTask - let expected = - RT.DList(VT.unknown, [ RT.DBool true; RT.DBool false; RT.DBool true ]) - return Expect.equal actual expected "" - } + t + "[true; false; true]" + E.boolList + (RT.DList(VT.unknown, [ RT.DBool true; RT.DBool false; RT.DBool true ])) let boolListList = - testTask "[[true; false]; [false; true]]" { - let! actual = eval E.boolListList |> Ply.toTask - let expected = - RT.DList( - VT.unknown, - [ RT.DList(VT.unknown, [ RT.DBool true; RT.DBool false ]) - RT.DList(VT.unknown, [ RT.DBool false; RT.DBool true ]) ] - ) - return Expect.equal actual expected "" - } -let letSimple = - testTask "let x = true\nx" { - let! actual = eval E.letSimple |> Ply.toTask - let expected = RT.DBool true - return Expect.equal actual expected "" - } -let letTuple = - testTask "let (x, y) = (1, 2)\nx" { - let! actual = eval E.letTuple |> Ply.toTask - let expected = RT.DInt64 1L - return Expect.equal actual expected "" - } + t + "[[true; false]; [false; true]]" + E.boolListList + (RT.DList( + VT.unknown, + [ RT.DList(VT.unknown, [ RT.DBool true; RT.DBool false ]) + RT.DList(VT.unknown, [ RT.DBool false; RT.DBool true ]) ] + )) +let letSimple = t "let x = true\nx" E.letSimple (RT.DBool true) +let letTuple = t "let (x, y) = (1, 2)\nx" E.letTuple (RT.DInt64 1L) let letTupleNested = - testTask "let (a, (b, c)) = (1, (2, 3))\nb" { - let! actual = eval E.letTupleNested |> Ply.toTask - let expected = RT.DInt64 2L - return Expect.equal actual expected "" - } + t "let (a, (b, c)) = (1, (2, 3))\nb" E.letTupleNested (RT.DInt64 2L) -let simpleString = - testTask "[\"hello\"]" { - let! actual = eval E.simpleString |> Ply.toTask - let expected = RT.DString "hello" - return Expect.equal actual expected "" - } +let simpleString = t "[\"hello\"]" E.simpleString (RT.DString "hello") let stringWithInterpolation = - testTask "[let x = \"world\" in $\"hello {x}\"]" { - let! actual = eval E.stringWithInterpolation |> Ply.toTask - let expected = RT.DString "hello, world" - return Expect.equal actual expected "" - } + t + "[let x = \"world\" in $\"hello {x}\"]" + E.stringWithInterpolation + (RT.DString "hello, world") -let dictEmpty = - testTask "Dict {}" { - let! actual = eval E.dictEmpty |> Ply.toTask - let expected = RT.DDict(VT.unknown, Map.empty) - return Expect.equal actual expected "" - } +let dictEmpty = t "Dict {}" E.dictEmpty (RT.DDict(VT.unknown, Map.empty)) let dictSimple = - testTask "Dict { t: true}" { - let! actual = eval E.dictSimple |> Ply.toTask - let expected = RT.DDict(VT.unknown, Map [ "key", RT.DBool true ]) - return Expect.equal actual expected "" - } + t + "Dict { t: true}" + E.dictSimple + (RT.DDict(VT.unknown, Map [ "key", RT.DBool true ])) let dictMultEntries = - testTask "Dict {t: true; f: false}" { - let! actual = eval E.dictMultEntries |> Ply.toTask - let expected = - RT.DDict(VT.unknown, Map [ "t", RT.DBool true; "f", RT.DBool false ]) - return Expect.equal actual expected "" - } + t + "Dict {t: true; f: false}" + E.dictMultEntries + (RT.DDict(VT.unknown, Map [ "t", RT.DBool true; "f", RT.DBool false ])) let dictDupeKey = - testTask "Dict {t: true; f: false; t: false}" { - let! actual = eval E.dictDupeKey |> Ply.toTask - let expected = - RT.DDict(VT.unknown, Map [ "t", RT.DBool false; "f", RT.DBool false ]) - return Expect.equal actual expected "" - } + t + "Dict {t: true; f: false; t: false}" + E.dictDupeKey + (RT.DDict(VT.unknown, Map [ "t", RT.DBool false; "f", RT.DBool false ])) -let ifGotoThenBranch = - testTask "if true then 1 else 2" { - let! actual = eval E.ifGotoThenBranch |> Ply.toTask - let expected = RT.DInt64 1L - return Expect.equal actual expected "" - } +let ifGotoThenBranch = t "if true then 1 else 2" E.ifGotoThenBranch (RT.DInt64 1L) -let ifGotoElseBranch = - testTask "if false then 1 else 2" { - let! actual = eval E.ifGotoElseBranch |> Ply.toTask - let expected = RT.DInt64 2L - return Expect.equal actual expected "" - } -let ifElseMissing = - testTask "if false then 1" { - let! actual = eval E.ifElseMissing |> Ply.toTask - let expected = RT.DUnit - return Expect.equal actual expected "" - } +let ifGotoElseBranch = t "if false then 1 else 2" E.ifGotoElseBranch (RT.DInt64 2L) +let ifElseMissing = t "if false then 1" E.ifElseMissing RT.DUnit let tuple2 = - testTask "(false, true)" { - let! actual = eval E.tuple2 |> Ply.toTask - let expected = RT.DTuple(RT.DBool false, RT.DBool true, []) - return Expect.equal actual expected "" - } + t "(false, true)" E.tuple2 (RT.DTuple(RT.DBool false, RT.DBool true, [])) let tuple3 = - testTask "(false, true, false)" { - let! actual = eval E.tuple3 |> Ply.toTask - let expected = RT.DTuple(RT.DBool false, RT.DBool true, [ RT.DBool false ]) - return Expect.equal actual expected "" - } - + t + "(false, true, false)" + E.tuple3 + (RT.DTuple(RT.DBool false, RT.DBool true, [ RT.DBool false ])) let tupleNested = - testTask "((false, true), true, (true, false)))" { - let! actual = eval E.tupleNested |> Ply.toTask - let expected = - RT.DTuple( - RT.DTuple(RT.DBool false, RT.DBool true, []), - RT.DBool true, - [ RT.DTuple(RT.DBool true, RT.DBool false, []) ] - ) - return Expect.equal actual expected "" - } - -// let TODO = -// testTask "TODO" { -// let! actual = eval E.TODO |> Ply.toTask -// let expected = RT.DUnit -// return Expect.equal actual expected "" -// } + t + "((false, true), true, (true, false)))" + E.tupleNested + (RT.DTuple( + RT.DTuple(RT.DBool false, RT.DBool true, []), + RT.DBool true, + [ RT.DTuple(RT.DBool true, RT.DBool false, []) ] + )) + +let matchSimple = + t + "match true with\n| false -> \"first branch\"\n| true -> \"second branch\"" + E.matchSimple + (RT.DString "second branch") let tests = @@ -183,4 +126,5 @@ let tests = ifElseMissing tuple2 tuple3 - tupleNested ] + tupleNested + matchSimple ] diff --git a/backend/tests/Tests/PT2RT.Tests.fs b/backend/tests/Tests/PT2RT.Tests.fs index 99556edfec..10b0038cb9 100644 --- a/backend/tests/Tests/PT2RT.Tests.fs +++ b/backend/tests/Tests/PT2RT.Tests.fs @@ -10,484 +10,389 @@ module VT = RT.ValueType module PT2RT = LibExecution.ProgramTypesToRuntimeTypes module PackageIDs = LibExecution.PackageIDs +open TestUtils.PTShortcuts + // TODO: consider adding an Expect.equalInstructions, // which better points out the diffs in the lists module Expressions = - let one = PT.EInt64(gid (), 1) + let one = eInt64 1 - let onePlusTwo : PT.Expr = - PT.EApply( - gid (), - PT.EFnName(gid (), Ok(PT.FQFnName.fqBuiltIn "int64Add" 0)), - [], - (NEList.ofList (PT.EInt64(gid (), 1)) [ PT.EInt64(gid (), 2) ]) - ) + let onePlusTwo = + eApply + (PT.EFnName(gid (), Ok(PT.FQFnName.fqBuiltIn "int64Add" 0))) + [] + [ eInt64 1; eInt64 2 ] // TODO: try to use undefined variable // TODO: lpunit - let letSimple : PT.Expr = - PT.ELet( - gid (), - PT.LPVariable(gid (), "x"), - PT.EBool(gid (), true), - PT.EVariable(gid (), "x") - ) - let letTuple : PT.Expr = - PT.ELet( - gid (), - PT.LPTuple(gid (), PT.LPVariable(gid (), "x"), PT.LPVariable(gid (), "y"), []), - PT.ETuple(gid (), PT.EInt64(gid (), 1), PT.EInt64(gid (), 2), []), - PT.EVariable(gid (), "x") - ) + let letSimple = eLet (lpVar "x") (eBool true) (eVar "x") + let letTuple = + eLet + (lpTuple (lpVar "x") (lpVar "y") []) + (eTuple (eInt64 1) (eInt64 2) []) + (eVar "x") /// `let (a, (b, c)) = (1, (2, 3)) in b` - let letTupleNested : PT.Expr = - PT.ELet( - gid (), - PT.LPTuple( - gid (), - PT.LPVariable(gid (), "a"), - PT.LPTuple( - gid (), - PT.LPVariable(gid (), "b"), - PT.LPVariable(gid (), "c"), - [] - ), - [] - ), - PT.ETuple( - gid (), - PT.EInt64(gid (), 1), - PT.ETuple(gid (), PT.EInt64(gid (), 2), PT.EInt64(gid (), 3), []), - [] - ), - PT.EVariable(gid (), "b") - ) - - let boolList : PT.Expr = - PT.EList( - gid (), - [ PT.EBool(gid (), true); PT.EBool(gid (), false); PT.EBool(gid (), true) ] - ) - - let boolListList : PT.Expr = - PT.EList( - gid (), - [ PT.EList(gid (), [ PT.EBool(gid (), true); PT.EBool(gid (), false) ]) - PT.EList(gid (), [ PT.EBool(gid (), false); PT.EBool(gid (), true) ]) ] - ) - - let simpleString : PT.Expr = PT.EString(gid (), [ PT.StringText("hello") ]) - - let stringWithInterpolation : PT.Expr = - PT.ELet( - gid (), - PT.LPVariable(gid (), "x"), - PT.EString(gid (), [ PT.StringText ", world" ]), - PT.EString( - gid (), - [ PT.StringText "hello"; PT.StringInterpolation(PT.EVariable(gid (), "x")) ] - ) - ) - - let dictEmpty : PT.Expr = PT.EDict(gid (), []) - let dictSimple : PT.Expr = PT.EDict(gid (), [ "key", PT.EBool(gid (), true) ]) - let dictMultEntries : PT.Expr = - PT.EDict(gid (), [ "t", PT.EBool(gid (), true); "f", PT.EBool(gid (), false) ]) - let dictDupeKey : PT.Expr = - PT.EDict( - gid (), - [ "t", PT.EBool(gid (), true) - "f", PT.EBool(gid (), false) - "t", PT.EBool(gid (), false) ] - ) - - let ifGotoThenBranch : PT.Expr = - PT.EIf( - gid (), - PT.EBool(gid (), true), - PT.EInt64(gid (), 1), - Some(PT.EInt64(gid (), 2)) - ) - let ifGotoElseBranch : PT.Expr = - PT.EIf( - gid (), - PT.EBool(gid (), false), - PT.EInt64(gid (), 1), - Some(PT.EInt64(gid (), 2)) - ) - let ifElseMissing : PT.Expr = - PT.EIf(gid (), PT.EBool(gid (), false), PT.EInt64(gid (), 1), None) + let letTupleNested = + eLet + (lpTuple (lpVar "a") (lpTuple (lpVar "b") (lpVar "c") []) []) + (eTuple (eInt64 1) (eTuple (eInt64 2) (eInt64 3) []) []) + (eVar "b") + + let boolList = eList [ eBool true; eBool false; eBool true ] + + let boolListList = + eList [ eList [ eBool true; eBool false ]; eList [ eBool false; eBool true ] ] + + let simpleString = eStr [ strText "hello" ] + + let stringWithInterpolation = + eLet + (lpVar "x") + (eStr [ strText ", world" ]) + (eStr [ strText "hello"; strInterp (eVar "x") ]) + + let dictEmpty = eDict [] + let dictSimple = eDict [ "key", eBool true ] + let dictMultEntries = eDict [ "t", eBool true; "f", eBool false ] + let dictDupeKey = eDict [ "t", eBool true; "f", eBool false; "t", eBool false ] + + let ifGotoThenBranch = eIf (eBool true) (eInt64 1) (Some(eInt64 2)) + let ifGotoElseBranch = eIf (eBool false) (eInt64 1) (Some(eInt64 2)) + let ifElseMissing = eIf (eBool false) (eInt64 1) None /// (false, true) - let tuple2 : PT.Expr = - PT.ETuple(gid (), PT.EBool(gid (), false), PT.EBool(gid (), true), []) + let tuple2 = eTuple (eBool false) (eBool true) [] /// (false, true, false) - let tuple3 : PT.Expr = - PT.ETuple( - gid (), - PT.EBool(gid (), false), - PT.EBool(gid (), true), - [ PT.EBool(gid (), false) ] - ) + let tuple3 = eTuple (eBool false) (eBool true) [ eBool false ] /// ((false, true), true, (true, false)) - let tupleNested : PT.Expr = - PT.ETuple( - gid (), - PT.ETuple(gid (), PT.EBool(gid (), false), PT.EBool(gid (), true), []), - PT.EBool(gid (), true), - [ PT.ETuple(gid (), PT.EBool(gid (), true), PT.EBool(gid (), false), []) ] - ) + let tupleNested = + eTuple + (eTuple (eBool false) (eBool true) []) + (eBool true) + [ eTuple (eBool true) (eBool false) [] ] + + let matchSimple = + eMatch + (eBool true) + [ { pat = PT.MPBool(gid (), false) + whenCondition = None + rhs = eStr [ strText "first branch" ] } + { pat = PT.MPBool(gid (), true) + whenCondition = None + rhs = eStr [ strText "second branch" ] } ] module E = Expressions -let one = - testTask "1" { - let actual = PT2RT.Expr.toRT 0 E.one - let expected = (1, [ RT.LoadVal(0, RT.DInt64 1L) ], 0) +let t name expr expected = + testTask name { + let actual = PT2RT.Expr.toRT 0 expr return Expect.equal actual expected "" } +let one = t "1" E.one (1, [ RT.LoadVal(0, RT.DInt64 1L) ], 0) + let onePlusTwo = - testTask "1+2" { - let actual = PT2RT.Expr.toRT 0 E.onePlusTwo - - let expected = - (4, - [ RT.LoadVal( - 0, - RT.DFnVal( - RT.NamedFn(RT.FQFnName.Builtin { name = "int64Add"; version = 0 }) - ) + t + "1+2" + E.onePlusTwo + (4, + [ RT.LoadVal( + 0, + RT.DFnVal( + RT.NamedFn(RT.FQFnName.Builtin { name = "int64Add"; version = 0 }) ) - RT.LoadVal(1, RT.DInt64 1L) - RT.LoadVal(2, RT.DInt64 2L) - RT.Apply(3, 0, [], { head = 1; tail = [ 2 ] }) ], - 3) - - return Expect.equal actual expected "" - } + ) + RT.LoadVal(1, RT.DInt64 1L) + RT.LoadVal(2, RT.DInt64 2L) + RT.Apply(3, 0, [], { head = 1; tail = [ 2 ] }) ], + 3) let letSimple = - testTask "let x = true\n x" { - let actual = PT2RT.Expr.toRT 0 E.letSimple - - let expected = - (2, - [ RT.LoadVal(0, RT.DBool true) - RT.SetVar("x", 0) // where the 'true' is stored - RT.GetVar(1, "x") ], - 1) - - return Expect.equal actual expected "" - } + t + "let x = true\n x" + E.letSimple + (2, + [ RT.LoadVal(0, RT.DBool true) + RT.SetVar("x", 0) // where the 'true' is stored + RT.GetVar(1, "x") ], + 1) let letTuple = - testTask "let (x, y) = (1, 2)\nx" { - let actual = PT2RT.Expr.toRT 0 E.letTuple - - let expected = - (6, - [ // register 0 isn't exposed, but used to temporarily store the tuple - RT.LoadVal(1, RT.DInt64 1L) - RT.LoadVal(2, RT.DInt64 2L) - RT.CreateTuple(0, 1, 2, []) - RT.ExtractTupleItems(0, 3, 4, []) - - RT.SetVar("x", 3) - RT.SetVar("y", 4) - - RT.GetVar(5, "x") ], - 5) - - return Expect.equal actual expected "" - } + t + "let (x, y) = (1, 2)\nx" + E.letTuple + (6, + [ // register 0 isn't exposed, but used to temporarily store the tuple + RT.LoadVal(1, RT.DInt64 1L) + RT.LoadVal(2, RT.DInt64 2L) + RT.CreateTuple(0, 1, 2, []) + RT.ExtractTupleItems(0, 3, 4, []) + + RT.SetVar("x", 3) + RT.SetVar("y", 4) + + RT.GetVar(5, "x") ], + 5) let letTupleNested = - testTask "let (a, (b, c)) = (1, (2, 3)) in b" { - let actual = PT2RT.Expr.toRT 0 E.letTupleNested - - let expected = - (10, - [ // reserve 0 for outer tuple - RT.LoadVal(1, RT.DInt64 1L) - // reserve 2 for inner tuple - RT.LoadVal(3, RT.DInt64 2L) - RT.LoadVal(4, RT.DInt64 3L) - RT.CreateTuple(2, 3, 4, []) // create inner tuple - RT.CreateTuple(0, 1, 2, []) // create outer tuple - RT.ExtractTupleItems(0, 5, 6, []) // extract outer tuple items - RT.SetVar("a", 5) - RT.ExtractTupleItems(6, 7, 8, []) - RT.SetVar("b", 7) - RT.SetVar("c", 8) - RT.GetVar(9, "b") ], - 9) - - return Expect.equal actual expected "" - } + t + "let (a, (b, c)) = (1, (2, 3)) in b" + E.letTupleNested + (10, + [ // reserve 0 for outer tuple + RT.LoadVal(1, RT.DInt64 1L) + // reserve 2 for inner tuple + RT.LoadVal(3, RT.DInt64 2L) + RT.LoadVal(4, RT.DInt64 3L) + RT.CreateTuple(2, 3, 4, []) // create inner tuple + RT.CreateTuple(0, 1, 2, []) // create outer tuple + RT.ExtractTupleItems(0, 5, 6, []) // extract outer tuple items + RT.SetVar("a", 5) + RT.ExtractTupleItems(6, 7, 8, []) + RT.SetVar("b", 7) + RT.SetVar("c", 8) + RT.GetVar(9, "b") ], + 9) let boolList = - testTask "[true, false, true]" { - let actual = PT2RT.Expr.toRT 0 E.boolList + t + "[true, false, true]" + E.boolList + (4, + [ RT.LoadVal(0, RT.DList(VT.unknown, [])) - let expected = - (4, - [ RT.LoadVal(0, RT.DList(VT.unknown, [])) + RT.LoadVal(1, RT.DBool true) + RT.AddItemToList(0, 1) - RT.LoadVal(1, RT.DBool true) - RT.AddItemToList(0, 1) + RT.LoadVal(2, RT.DBool false) + RT.AddItemToList(0, 2) - RT.LoadVal(2, RT.DBool false) - RT.AddItemToList(0, 2) - - RT.LoadVal(3, RT.DBool true) - RT.AddItemToList(0, 3) ], - 0) - - return Expect.equal actual expected "" - } + RT.LoadVal(3, RT.DBool true) + RT.AddItemToList(0, 3) ], + 0) let boolListList = - testTask "[[true; false]; [false; true]]" { - let actual = PT2RT.Expr.toRT 0 E.boolListList - - let expected = - (7, - [ // create outer list - RT.LoadVal(0, RT.DList(VT.unknown, [])) - - // first inner list - RT.LoadVal(1, RT.DList(VT.unknown, [])) - RT.LoadVal(2, RT.DBool true) - RT.AddItemToList(1, 2) - RT.LoadVal(3, RT.DBool false) - RT.AddItemToList(1, 3) - // add it to outer - RT.AddItemToList(0, 1) - - // second inner list - RT.LoadVal(4, RT.DList(VT.unknown, [])) - RT.LoadVal(5, RT.DBool false) - RT.AddItemToList(4, 5) - RT.LoadVal(6, RT.DBool true) - RT.AddItemToList(4, 6) - // add it to outer - RT.AddItemToList(0, 4) ], - 0) + t + "[[true; false]; [false; true]]" + E.boolListList + (7, + [ // create outer list + RT.LoadVal(0, RT.DList(VT.unknown, [])) + + // first inner list + RT.LoadVal(1, RT.DList(VT.unknown, [])) + RT.LoadVal(2, RT.DBool true) + RT.AddItemToList(1, 2) + RT.LoadVal(3, RT.DBool false) + RT.AddItemToList(1, 3) + // add it to outer + RT.AddItemToList(0, 1) + + // second inner list + RT.LoadVal(4, RT.DList(VT.unknown, [])) + RT.LoadVal(5, RT.DBool false) + RT.AddItemToList(4, 5) + RT.LoadVal(6, RT.DBool true) + RT.AddItemToList(4, 6) + // add it to outer + RT.AddItemToList(0, 4) ], + 0) - return Expect.equal actual expected "" - } let simpleString = - testTask "[\"hello\"]" { - let actual = PT2RT.Expr.toRT 0 E.simpleString - - let expected = - (2, - [ RT.LoadVal(0, RT.DString "") - RT.LoadVal(1, RT.DString "hello") - RT.AppendString(0, 1) ], - 0) - - return Expect.equal actual expected "" - } + t + "[\"hello\"]" + E.simpleString + (2, + [ RT.LoadVal(0, RT.DString "") + RT.LoadVal(1, RT.DString "hello") + RT.AppendString(0, 1) ], + 0) let stringWithInterpolation = - testTask "[let x = \"world\"\n$\"hello {x}\"]" { - let actual = PT2RT.Expr.toRT 0 E.stringWithInterpolation - - let expected = - (5, - [ RT.LoadVal(0, RT.DString "") - RT.LoadVal(1, RT.DString ", world") - RT.AppendString(0, 1) - RT.SetVar("x", 0) - RT.LoadVal(2, RT.DString "") - RT.LoadVal(3, RT.DString "hello") - RT.AppendString(2, 3) - RT.GetVar(4, "x") - RT.AppendString(2, 4) ], - 2) + t + "[let x = \"world\"\n$\"hello {x}\"]" + E.stringWithInterpolation + (5, + [ RT.LoadVal(0, RT.DString "") + RT.LoadVal(1, RT.DString ", world") + RT.AppendString(0, 1) + RT.SetVar("x", 0) + RT.LoadVal(2, RT.DString "") + RT.LoadVal(3, RT.DString "hello") + RT.AppendString(2, 3) + RT.GetVar(4, "x") + RT.AppendString(2, 4) ], + 2) - return Expect.equal actual expected "" - } let dictEmpty = - testTask "Dict {}" { - let actual = PT2RT.Expr.toRT 0 E.dictEmpty - - let expected = (1, [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) ], 0) - - return Expect.equal actual expected "" - } + t "Dict {}" E.dictEmpty (1, [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) ], 0) let dictSimple = - testTask "Dict { t: true}" { - let actual = PT2RT.Expr.toRT 0 E.dictSimple - - let expected = - (2, - [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) - RT.LoadVal(1, RT.DBool true) - RT.AddDictEntry(0, "key", 1) ], - 0) - - return Expect.equal actual expected "" - } + t + "Dict { t: true}" + E.dictSimple + (2, + [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) + RT.LoadVal(1, RT.DBool true) + RT.AddDictEntry(0, "key", 1) ], + 0) let dictMultEntries = - testTask "Dict {t: true; f: false}" { - let actual = PT2RT.Expr.toRT 0 E.dictMultEntries - - let expected = - (3, - [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) - RT.LoadVal(1, RT.DBool true) - RT.AddDictEntry(0, "t", 1) - RT.LoadVal(2, RT.DBool false) - RT.AddDictEntry(0, "f", 2) ], - 0) - - return Expect.equal actual expected "" - } + t + "Dict {t: true; f: false}" + E.dictMultEntries + (3, + [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) + RT.LoadVal(1, RT.DBool true) + RT.AddDictEntry(0, "t", 1) + RT.LoadVal(2, RT.DBool false) + RT.AddDictEntry(0, "f", 2) ], + 0) let dictDupeKey = - testTask "Dict {t: true; f: false; t: true}" { - let actual = PT2RT.Expr.toRT 0 E.dictDupeKey - - let expected = - (4, - [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) - RT.LoadVal(1, RT.DBool true) - RT.AddDictEntry(0, "t", 1) - RT.LoadVal(2, RT.DBool false) - RT.AddDictEntry(0, "f", 2) - RT.LoadVal(3, RT.DBool false) - RT.AddDictEntry(0, "t", 3) ], - 0) - - return Expect.equal actual expected "" - } + t + "Dict {t: true; f: false; t: true}" + E.dictDupeKey + (4, + [ RT.LoadVal(0, RT.DDict(VT.unknown, Map.empty)) + RT.LoadVal(1, RT.DBool true) + RT.AddDictEntry(0, "t", 1) + RT.LoadVal(2, RT.DBool false) + RT.AddDictEntry(0, "f", 2) + RT.LoadVal(3, RT.DBool false) + RT.AddDictEntry(0, "t", 3) ], + 0) let ifGotoThenBranch = - testTask "if true then 1 else 2" { - let actual = PT2RT.Expr.toRT 0 E.ifGotoThenBranch + t + "if true then 1 else 2" + E.ifGotoThenBranch + (4, + [ // cond + RT.LoadVal(1, RT.DBool true) + RT.JumpByIfFalse(3, 1) - let expected = - (4, - [ // cond - RT.LoadVal(1, RT.DBool true) - RT.JumpByIfFalse(3, 1) + // then + RT.LoadVal(2, RT.DInt64 1L) + RT.CopyVal(0, 2) + RT.JumpBy 2 - // then - RT.LoadVal(2, RT.DInt64 1L) - RT.CopyVal(0, 2) - RT.JumpBy 2 + // else + RT.LoadVal(3, RT.DInt64 2L) + RT.CopyVal(0, 3) ], + 0) - // else - RT.LoadVal(3, RT.DInt64 2L) - RT.CopyVal(0, 3) ], - 0) - - return Expect.equal actual expected "" - } let ifGotoElseBranch = - testTask "if false then 1 else 2" { - let actual = PT2RT.Expr.toRT 0 E.ifGotoElseBranch - - let expected = - (4, - [ // cond - RT.LoadVal(1, RT.DBool false) - RT.JumpByIfFalse(3, 1) + t + "if false then 1 else 2" + E.ifGotoElseBranch + (4, + [ // cond + RT.LoadVal(1, RT.DBool false) + RT.JumpByIfFalse(3, 1) - // then - RT.LoadVal(2, RT.DInt64 1L) - RT.CopyVal(0, 2) - RT.JumpBy 2 + // then + RT.LoadVal(2, RT.DInt64 1L) + RT.CopyVal(0, 2) + RT.JumpBy 2 - // else - RT.LoadVal(3, RT.DInt64 2L) - RT.CopyVal(0, 3) ], - 0) + // else + RT.LoadVal(3, RT.DInt64 2L) + RT.CopyVal(0, 3) ], + 0) - return Expect.equal actual expected "" - } let ifElseMissing = - testTask "if false then 1" { - let actual = PT2RT.Expr.toRT 0 E.ifElseMissing - - let expected = - (3, - [ RT.LoadVal(0, RT.DUnit) - RT.LoadVal(1, RT.DBool false) - RT.JumpByIfFalse(2, 1) - RT.LoadVal(2, RT.DInt64 1L) - RT.CopyVal(0, 2) ], - 0) + t + "if false then 1" + E.ifElseMissing + (3, + [ RT.LoadVal(0, RT.DUnit) + RT.LoadVal(1, RT.DBool false) + RT.JumpByIfFalse(2, 1) + RT.LoadVal(2, RT.DInt64 1L) + RT.CopyVal(0, 2) ], + 0) - return Expect.equal actual expected "" - } let tuple2 = - testTask "(false, true)" { - let actual = PT2RT.Expr.toRT 0 E.tuple2 - - let expected = - (3, - [ RT.LoadVal(1, RT.DBool false) - RT.LoadVal(2, RT.DBool true) - RT.CreateTuple(0, 1, 2, []) ], - 0) - - return Expect.equal actual expected "" - } + t + "(false, true)" + E.tuple2 + (3, + [ RT.LoadVal(1, RT.DBool false) + RT.LoadVal(2, RT.DBool true) + RT.CreateTuple(0, 1, 2, []) ], + 0) let tuple3 = - testTask "(false, true, false)" { - let actual = PT2RT.Expr.toRT 0 E.tuple3 - - let expected = - (4, - [ RT.LoadVal(1, RT.DBool false) - RT.LoadVal(2, RT.DBool true) - RT.LoadVal(3, RT.DBool false) - RT.CreateTuple(0, 1, 2, [ 3 ]) ], - 0) - - return Expect.equal actual expected "" - } + t + "(false, true, false)" + E.tuple3 + (4, + [ RT.LoadVal(1, RT.DBool false) + RT.LoadVal(2, RT.DBool true) + RT.LoadVal(3, RT.DBool false) + RT.CreateTuple(0, 1, 2, [ 3 ]) ], + 0) let tupleNested = - testTask "((false, true), true, (true, false))" { - let actual = PT2RT.Expr.toRT 0 E.tupleNested - - let expected = - (8, - [ // 0 "reserved" for outer tuple - - // first inner tuple (1 "reserved") - RT.LoadVal(2, RT.DBool false) - RT.LoadVal(3, RT.DBool true) - RT.CreateTuple(1, 2, 3, []) - - // middle value - RT.LoadVal(4, RT.DBool true) - - // second inner tuple (5 "reserved") - RT.LoadVal(6, RT.DBool true) - RT.LoadVal(7, RT.DBool false) - RT.CreateTuple(5, 6, 7, []) - - // wrap all in outer tuple - RT.CreateTuple(0, 1, 4, [ 5 ]) ], - 0) - - return Expect.equal actual expected "" - } + t + "((false, true), true, (true, false))" + E.tupleNested + (8, + [ // 0 "reserved" for outer tuple + + // first inner tuple (1 "reserved") + RT.LoadVal(2, RT.DBool false) + RT.LoadVal(3, RT.DBool true) + RT.CreateTuple(1, 2, 3, []) + + // middle value + RT.LoadVal(4, RT.DBool true) + + // second inner tuple (5 "reserved") + RT.LoadVal(6, RT.DBool true) + RT.LoadVal(7, RT.DBool false) + RT.CreateTuple(5, 6, 7, []) + + // wrap all in outer tuple + RT.CreateTuple(0, 1, 4, [ 5 ]) ], + 0) + +let matchSimple = + t + "match true with\n| false -> \"first branch\"\n| true -> \"second branch\"" + E.matchSimple + (4, + [ // handle the value we're matching on + RT.LoadVal(0, RT.DBool true) + + // FIRST BRANCH + RT.MatchValue(0, RT.MPBool false, 5) + // rhs + RT.LoadVal(2, RT.DString "") + RT.LoadVal(3, RT.DString "first branch") + RT.AppendString(2, 3) + RT.CopyVal(1, 2) + RT.JumpBy 7 + + // SECOND BRANCH + RT.MatchValue(0, RT.MPBool true, 5) + // rhs + RT.LoadVal(2, RT.DString "") + RT.LoadVal(3, RT.DString "second branch") + RT.AppendString(2, 3) + RT.CopyVal(1, 2) + RT.JumpBy 1 + + // handle the case where no branches match + RT.MatchUnmatched ], + 1) let tests = testList @@ -510,4 +415,5 @@ let tests = ifElseMissing tuple2 tuple3 - tupleNested ] + tupleNested + matchSimple ] diff --git a/tree-sitter-darklang/package-lock.json b/tree-sitter-darklang/package-lock.json index f2cf375bca..b15741a1bd 100644 --- a/tree-sitter-darklang/package-lock.json +++ b/tree-sitter-darklang/package-lock.json @@ -28,7 +28,6 @@ "integrity": "sha512-XjTcS3wdTy/2cc/ptMLc/WRyOLECRYcMTrSWyhZnj1oGSOWbHLTklgsgRICU3cPfb0vy+oZCC33M43u6R1HSCA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "tree-sitter": "cli.js" }